我最近开始用EXPO开发一款Reaction Native App,几天前我遇到了一个问题,现在开始让我发疯.

我想向OpenAI TTS API发送一个文本字符串,并立即在我的应用程序中播放音频响应.我不想将文件保存在本地.接口链接:https://platform.openai.com/docs/guides/text-to-speech

到目前为止,我已经成功地发出了HTTP请求,看起来我从API得到了正确的响应.当我输出response.blob()时,我得到:

{"_data": {"__collector": {}, "blobId": "5C362F63-71CB-45EC-913E-9A808AF2194F", "name": "speech.mp3", "offset": 0, "size": 143520, "type": "audio/mpeg"}}

这个问题似乎是播放声音与AV.我已经在谷歌上搜索了好几天了,无论我try 哪种解决方案,我在try 播放声音时都会遇到一些错误.当前代码的当前错误是:

错误:AVPlayerItem实例失败,错误代码为-1002,域为"NSURLErrorDOMAIN".

如果有人能帮我(请写代码示例),我将不胜感激.

我还读到了这篇文章,其中的解决方案是改用谷歌的TTS API,但这不是我想要的解决方案: https://www.reddit.com/r/reactnative/comments/13pa9wx/playing_blob_audio_using_expo_audio_react_native/

这是我的代码:

const convertTextToSpeech = async (textToConvert) => {

    const apiKey = 'myKey';
    const url = 'https://api.openai.com/v1/audio/speech';

    const requestOptions = {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            model: 'tts-1',
            input: textToConvert,
            voice: 'alloy',
            language: 'da',
        })
    };

    await fetch(url, requestOptions)
        .then(response => {
            if (!response.ok) {
                throw new Error('Network response was not ok');
            }
            return response.blob();
        })
        .then(blob => {
            playSound(blob);
        })
        .catch(error => {
            console.error('There was a problem with the request:', error);
        });
};

async function playSound(blob) {
    const url = URL.createObjectURL(blob);
    const { sound } = await Audio.Sound.createAsync({ uri: url });
    await sound.playAsync();
}

推荐答案

好的,经过一些认真的try ,这是我的解决方案,我希望它能很好地服务于你和其他人.目前对我来说是有效的(我仍在测试功能,所以它可能会遇到问题,但祈祷吧).

此外,除了播放声音实例以确保其工作外,我并没有处理更多的声音实例,所以可能会有更多的工作要做.

Client

const toBuffer = async (blob) => {
  const uri = await toDataURI(blob);
  const base64 = uri.replace(/^.*,/g, "");
  return Buffer.from(base64, "base64");
};

const toDataURI = (blob) =>
  new Promise((resolve) => {
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = () => {
      const uri = reader.result?.toString();
      resolve(uri);
    };
  });

const constructTempFilePath = async (buffer) => {
  const tempFilePath = FileSystem.cacheDirectory + "speech.mp3";
  await FileSystem.writeAsStringAsync(
    tempFilePath,
    buffer.toString("base64"),
    {
      encoding: FileSystem.EncodingType.Base64,
    }
  );

  return tempFilePath;
};

const blob = await response.blob();
const buffer = await toBuffer(blob);
const tempFilePath = await constructTempFilePath(buffer);
const { sound } = await Audio.Sound.createAsync({ uri: tempFilePath });
await sound.playAsync();

Server

const mp3 = await openai.audio.speech.create({
  model: "tts-1",
  voice: "alloy",
  input: "Hello World",
});

const mp3Stream = new PassThrough();
mp3Stream.end(Buffer.from(await mp3.arrayBuffer()));
res.setHeader("Content-Type", "audio/mpeg");
res.setHeader("Content-Disposition", 'attachment; filename="audio.mp3"');
mp3Stream.pipe(res);

React-native相关问答推荐

如何更改分隔底部标签和屏幕内容的线的 colored颜色 ?React Native Tab导航器

@testing-library/react-native -> 渲染 -> TypeError:无法读取未定义的属性(读取存在)

如何在react-navigation 中将drawer抽屉放在标题上?

我应该什么时候调用 realm.close()?

reactnavigation后退按钮的自定义后退导航

如何在react-native 中从 FlatList 中删除元素/索引?

如何在StackNavigator 中实现自定义标题图标?

Firebase检测用户是否存在

React-Native 构建失败的应用程序:mergeDebugAssets

如何将 View 定位在 ScrollView 的底部?

我们如何将 Firebase Analytics 与基于 expo 的 react-native 应用程序一起使用

React-Native: measure测量一个视图

react-native createbottomtabnavigator 隐藏标签栏标签

react-native 自动完成文本输入

在 React Native (iOS) 中支持动态类型

React Native vs Swift/Objective-C/Java Native

等待模块失效的超时

带有父 PanResponder 的 TouchableOpacity

在 React Native 中使用小数

Mapping children of a parent component