From 5619f1b3954aba2c956153f64d3f375bc097ae47 Mon Sep 17 00:00:00 2001 From: Michal Sek Date: Fri, 29 Nov 2024 16:54:18 +0100 Subject: [PATCH 1/2] feat: working on open ai example --- .gitignore | 3 + .../common-app/src/examples/OpenAI/OpenAI.tsx | 114 ++++++++++++++++++ apps/common-app/src/examples/OpenAI/index.ts | 1 + apps/common-app/src/examples/index.ts | 8 ++ apps/common-app/src/utils/env.ts | 3 + apps/fabric-example/babel.config.js | 2 +- apps/fabric-example/ios/Podfile.lock | 4 +- apps/fabric-example/package.json | 1 + yarn.lock | 19 +++ 9 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 apps/common-app/src/examples/OpenAI/OpenAI.tsx create mode 100644 apps/common-app/src/examples/OpenAI/index.ts create mode 100644 apps/common-app/src/utils/env.ts diff --git a/.gitignore b/.gitignore index 6d08e317..4bf942bb 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ react-native-audio-api*.tgz # Android .kotlin + +# Envs +.env diff --git a/apps/common-app/src/examples/OpenAI/OpenAI.tsx b/apps/common-app/src/examples/OpenAI/OpenAI.tsx new file mode 100644 index 00000000..311f143c --- /dev/null +++ b/apps/common-app/src/examples/OpenAI/OpenAI.tsx @@ -0,0 +1,114 @@ +import React, { useState, FC } from 'react'; +import { AudioBuffer, AudioContext } from 'react-native-audio-api'; +import { ActivityIndicator, TextInput, StyleSheet } from 'react-native'; + +import { Container, Button, Spacer } from '../../components'; +import Env from '../../utils/env'; +import { colors } from '../../styles'; + +async function getOpenAIResponse(input: string, voice: string = 'alloy') { + return await fetch('https://api.openai.com/v1/audio/speech', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${Env.openAiToken}`, + }, + body: JSON.stringify({ + model: 'tts-1-hd', + voice: voice, + input: input, + response_format: 'pcm', + }), + }).then((response) => response.arrayBuffer()); +} + +function goofyResample( + audioContext: AudioContext, + input: Int16Array +): AudioBuffer { + const outputBuffer = audioContext.createBuffer(2, input.length * 2, 48000); + const processingChannel: Array = []; + const upSampleChannel: Array = []; + + for (let i = 0; i < input.length; i += 1) { + processingChannel[i] = input[i] / 32768.0; + } + + for (let i = 0; i < input.length; i += 1) { + const isLast = i === input.length - 1; + const currentSample = processingChannel[i]; + const nextSample = isLast ? currentSample : processingChannel[i + 1]; + + upSampleChannel[2 * i] = currentSample; + upSampleChannel[2 * i + 1] = (currentSample + nextSample) / 2; + } + + outputBuffer.copyToChannel(upSampleChannel, 0); + outputBuffer.copyToChannel(upSampleChannel, 1); + + return outputBuffer; +} + +const OpenAI: FC = () => { + const [isLoading, setIsLoading] = useState(false); + const [textToRead, setTextToRead] = useState(''); + + const onTestOpenAI = async () => { + if (isLoading) { + return; + } + + const aCtx = new AudioContext(); + + setIsLoading(true); + const results = await getOpenAIResponse(textToRead, 'alloy'); + setIsLoading(false); + + const audioBuffer = goofyResample(aCtx, new Int16Array(results)); + const sourceNode = aCtx.createBufferSource(); + const duration = audioBuffer.duration; + const now = aCtx.currentTime; + + sourceNode.buffer = audioBuffer; + + sourceNode.connect(aCtx.destination); + + sourceNode.start(now); + sourceNode.stop(now + duration); + }; + + return ( + + + + +