我正在try 实现一种用户录制和发送他们的语音记录的方法.我已经设法让它工作(有点),但我不确定这是否是正确的方法.目前,当用户按住记录按钮时,我会创建一个新的Audio对象,初始化它,然后调用startAsync.下面是这段代码:

const recording = React.useRef<Audio.Recording | null>(null);

const startRecording = React.useCallback(async () => {
    try {
        recording.current = new Audio.Recording();

        recording.current.setOnRecordingStatusUpdate((...args) => {
            // some logic
        });

        recording.current.setProgressUpdateInterval(50);

        await recording.current.prepareToRecordAsync({
            isMeteringEnabled: true,
            android: {
                extension: '.mp3',
                outputFormat: AndroidOutputFormat.MPEG_4,
                audioEncoder: AndroidAudioEncoder.AAC,
                sampleRate: 44100,
                numberOfChannels: 2,
                bitRate: 128000,
            },
            ios: {
                extension: '.mp3',
                outputFormat: IOSOutputFormat.MPEG4AAC,
                audioQuality: IOSAudioQuality.MAX,
                sampleRate: 44100,
                numberOfChannels: 2,
                bitRate: 128000,
                linearPCMBitDepth: 16,
                linearPCMIsBigEndian: false,
                linearPCMIsFloat: false,
            },
            web: {
                mimeType: 'audio/webm',
                bitsPerSecond: 128000,
            },
        });

        await recording.current.startAsync();
    } catch (err) {
        console.log('Failed to start recording', err);
        recording.current = null;
    }
}, []);

所以这个比特有一些问题.每当用户想要录制一条语音消息(可能非常频繁)时,我必须创建一个新的Audio对象(new Audio.Recording()),然后做一些异步操作来准备它.

一旦用户完成了特定的录制,用户体验会受到进一步的打击:

const stopRecording = React.useCallback(async () => {
    await recording.current.stopAndUnloadAsync();
        
    const uri = recording.current.getURI();
    console.log('uri', uri);
    recording.current = null;
}, []);

我的问题是,是否可以实例化一次它,然后只是"清空"它,以便我可以重用相同的,准备好的对象?

推荐答案

查看Expo Audio.Recording API,我没有看到任何"reset/empty/clean"内置方法,以重用传统对象或重置的方式重用.

该类表示一段录音.创建此类的实例后,必须调用prepareToRecordAsync才能录制音频.录音完成后,请拨打stopAndUnloadAsync.请注意,在任何给定时间,在prepareToRecordAsyncstopAndUnloadAsync之间的状态下只允许存在一个记录器.

一旦使用stopAndUnloadAsync停止和卸载记录,Audio.Recording对象就不能再用于新的记录会话.您必须 for each 新的录制会话实例化一个新的Audio.Recording对象.

User Interaction          App Logic                 Expo Audio API
      │                        │                              │
      ├──── Press Record ──────>                              │
      │                        │                              │
      │                        ├─── new Audio.Recording() ────> 
      │                        │                              │
      │                        ├─ prepareToRecordAsync() ─────> 
      │                        │                              │
      │                        ├─────── startAsync() ─────────> 
      │                        │                              │
      │                        │<──── Recording Starts ───────┤ 
      │                        │                              │
      │<────── UX Update ──────┤                              │

虽然您无法在Expo应用程序中"清空"并直接重用Audio.Recording个对象,但通过有效管理录制会话的生命周期,并在录制准备和清理阶段提供清晰的用户体验,您可以获得流畅高效的用户体验.

例如,您可以在准备录制时显示一个加载指示器或短暂禁用录制按钮,向用户发出正在发生的信号.

在记录停止、卸载并检索URI后,请确保通过将记录对象设置为null来正确处理记录对象的清理(用于内存管理和避免潜在的泄漏).

const stopRecording = React.useCallback(async () => {
    if (recording.current) {
        await recording.current.stopAndUnloadAsync();
            
        const uri = recording.current.getURI();
        console.log('uri', uri);
        // Cleanup after retrieving URI
        recording.current = null;
    }
}, []);

您还可以考虑使用React的useStateuseReducer来管理录制状态,这可以提供一种更简化的方式来处理录制的生命周期状态(例如,空闲、录制、完成).

import React, { useState, useCallback } from 'react';
import { Audio } from 'expo-av';

// define state hooks for managing the recording state and the `Audio.Recording` objects
const MyRecorderComponent = () => {
    const [recording, setRecording] = useState<Audio.Recording | null>(null);
    const [recordState, setRecordState] = useState<'idle' | 'recording' | 'finished'>('idle');
};

const startRecording = useCallback(async () => {
    try {
        const newRecording = new Audio.Recording();
        await newRecording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
        await newRecording.startAsync();

        // Update state to reflect that recording has started
        setRecording(newRecording);
        setRecordState('recording');
    } catch (err) {
        console.error('Failed to start recording', err);
    }
}, []);

const stopRecording = useCallback(async () => {
    if (recording) {
        await recording.stopAndUnloadAsync();
        const uri = recording.getURI();
        console.log('Recording stopped and stored at', uri);

        // Reset state
        setRecording(null);
        setRecordState('finished');
    }
}, [recording]);

在组件中使用recordState来控制UI,根据应用程序是空闲、录制还是录制完成,显示不同的选项:

return (
    <div>
        {recordState === 'idle' && <button onClick={startRecording}>Start Recording</button>}
        {recordState === 'recording' && <button onClick={stopRecording}>Stop Recording</button>}
        {recordState === 'finished' && <p>Recording finished. Ready to record another.</p>}
    </div>
);

我正在制作一个类似于WhatsApp的录音功能.这意味着我不可能每次都调用await newRecording.prepareToRecordAsync.

然后,另一种方法是提前准备好记录对象,并重新使用它,最大限度地减少开始新记录时的延迟.如上所述,由于Expo API的约束,直接重用Audio.Recording对象是不可能的.因此,您需要简化录制过程以尽可能响应:这意味着尽可能多地预加载,并减少录制开始/停止阶段执行的操作.

当您的组件被装入时,或在应用流程中的某个关键点,请提前准备一个Audio.Recording对象.这样,当用户启动录制时,录制可以更快地开始.

在停止录制和准备下一个录制之间实现最小的延迟,以便应用程序始终为下一个用户操作做好准备.

使用React的useEffect来处理准备和清理,确保录制在需要时准备好,并且资源被适当释放.

import React, { useEffect, useState, useCallback } from 'react';
import { Audio } from 'expo-av';

const MyRecorderComponent = () => {
    const [recording, setRecording] = useState<Audio.Recording | null>(null);
    const [isReady, setIsReady] = useState(false);

    // Prepare the recording in advance
    useEffect(() => {
        const prepareRecording = async () => {
            const newRecording = new Audio.Recording();
            await newRecording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
            setRecording(newRecording);
            setIsReady(true);
        };

        prepareRecording();

        // Cleanup function to unload the recording when component unmounts
        return () => {
            if (recording) {
                recording.stopAndUnloadAsync();
            }
        };
    }, [recording]);

    const startRecording = useCallback(async () => {
        if (isReady && recording) {
            await recording.startAsync();
            // Other logic to handle recording start
        }
    }, [isReady, recording]);

    const stopRecording = useCallback(async () => {
        if (recording) {
            await recording.stopAndUnloadAsync();
            // Logic to handle after recording is stopped
            // Optionally, prepare the next recording immediately after stopping
            setIsReady(false);
            const newRecording = new Audio.Recording();
            await newRecording.prepareToRecordAsync(Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY);
            setRecording(newRecording);
            setIsReady(true);
        }
    }, [recording]);

    return (
        <div>
            {isReady && <button onClick={startRecording}>Start Recording</button>}
            {!isReady && <p>Preparing...</p>}
            {/* Additional UI for stopping recording and handling the recorded data */}
        </div>
    );
};

这将提前准备一个Audio.Recording个对象,并在录制停止后立即重新准备一个新的对象,让应用程序为下一次录制做好准备:在录制停止后立即这样做,而不是在新的录制开始前这样做,应该会增强应用程序的感知响应性.

React-native相关问答推荐

添加REACT-Native-SVG时出现错误-失败:构建失败并出现异常

无法解析 com.google.gms:google-services:4.3.15

使用 resizeMode 封面截断的图像

滚动到具有可变元素大小的 FlatList 中的某些索引的有效方法

在 iOS 模拟器中运行 React Native 应用程序时找不到 UMModuleRegistryAdapter.h

如何在获取请求中传递 POST 参数?

如何在 Crashlytics (Fabrics) 中有效地对非致命异常进行分组?

如何重置react-native 动画?

文本在 android 中被截断以响应本机

Undefined 不是判断 this.state.* 的对象

react-native 从右到左

如何从 stack.navigation 外部的组件中使用 navigation.navigate

React Native:如何获取文件大小、mime 类型和扩展名?

react-native android应用程序中的underlineColorAndroid is not a valid style property错误

如何在 React Native 中按住以 Select 文本

在Android上更改高度时react-native TextInput显示错误

如何使用 Expo 在真实的 iOS 设备上运行应用程序?

如何在 React Native 中等待 Alert 对话框的响应?

运行 expo start 时如何禁用在浏览器中打开 DevTools?

React Native:如何动态更改