Skip to content

Commit

Permalink
new openai text to speach tool
Browse files Browse the repository at this point in the history
SQUASHED: AUTO-COMMIT-doc-journal-2024-04-25.md-index.md,AUTO-COMMIT-src-components-tools-openai-audio-speech.html,AUTO-COMMIT-src-components-tools-openai-audio-speech.js,
  • Loading branch information
JensLincke committed Apr 26, 2024
1 parent b16abe1 commit 95b86e3
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
47 changes: 47 additions & 0 deletions doc/journal/2024-04-25.md/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## 2024-04-25 OpenAI text to speech
*Author: @JensLincke*

This should either go into a tool ore context menu


```javascript
import OpenAI from "demos/openai/openai.js"

let apiKey = await OpenAI.ensureSubscriptionKey()

let prompt = {
"model": "tts-1",
"input": that.value,
"voice": "alloy"
}


const url = "https://api.openai.com/v1/audio/speech";

const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify(prompt)
};

const ctx = new AudioContext();

let result = await fetch(url, requestOptions)


let audio = await result.arrayBuffer().then(arrayBuffer => ctx.decodeAudioData(arrayBuffer))


function playback() {
const playSound = ctx.createBufferSource();
playSound.buffer = audio;
playSound.connect(ctx.destination);
playSound.start(ctx.currentTime);
}

playback()
```

33 changes: 33 additions & 0 deletions src/components/tools/openai-audio-speech.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template id="openai-audio-speech">
<style data-src="/src/external/font-awesome/css/font-awesome.css"></style>
<style data-src="/templates/livelystyle.css"></style>
<style>
:host {


}
#editor {
max-height: calc(100%);
flex: 1;
}

#pane {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}

#player {
float: right
}

</style>
<div id="pane">
<div>
<button id="generate">generate</button>
<audio id="player" controls="true"></audio>
</div>
<lively-code-mirror id="editor"></lively-code-mirror>
</div>
</template>
110 changes: 110 additions & 0 deletions src/components/tools/openai-audio-speech.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import OpenAI from "demos/openai/openai.js"

import Morph from 'src/components/widgets/lively-morph.js';

export default class OpenaiAudioSpeech extends Morph {
async initialize() {
this.windowTitle = "OpenaiAudioSpeech";
this.registerButtons()
lively.html.registerKeys(this); // automatically installs handler for some methods

this.editor.value = this.getAttribute("value")

this.editor.addEventListener("editor-loaded", () => {
this.editor.setOption('lineWrapping', true)

this.editor.editor.on("change", cm => {
this.setAttribute("value", this.editor.value)
})

})

}

get editor() {
return this.get("#editor")
}

get player() {
return this.get("#player")
}


get text() {
return this.editor.value
}


setupMediaSource() {
const mediaSource = new MediaSource();
const audio = this.player;
audio.controls = true;
audio.src = URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', () => {
console.log("sourceopen")
const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg'); // Use the appropriate MIME type

// Fetch the data and append it to the source buffer
this.fetchDataAndAppend(mediaSource, sourceBuffer);
});

audio.play()

return audio;
}

// This function fetches audio data using POST and appends chunks to the source buffer.
async fetchDataAndAppend(mediaSource, sourceBuffer) {

let apiKey = await OpenAI.ensureSubscriptionKey()

let prompt = {
"model": "tts-1",
"input": this.text,
"voice": "alloy"
}


const url = "https://api.openai.com/v1/audio/speech";

const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`
},
body: JSON.stringify(prompt)
};


const response = await fetch(url, requestOptions)
const reader = response.body.getReader();

// Function to handle reading each chunk
function process({ done, value }) {
if (done) {
mediaSource.endOfStream(); // Properly call endOfStream on the MediaSource instance
return;
}
if (sourceBuffer.updating) {
// If buffer is still updating, wait before appending more data
setTimeout(() => reader.read().then(process), 100);
} else {
sourceBuffer.appendBuffer(value);
reader.read().then(process);
}
}

// Start processing the stream
reader.read().then(process).catch(error => {
console.error('Error fetching or processing data:', error);
mediaSource.endOfStream('network'); // Signal an error in fetching stream
});
}

async onGenerate() {
// Call this function to start the process.
this.setupMediaSource();
}
}

0 comments on commit 95b86e3

Please sign in to comment.