Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c0b32b85e | |||
| 01665a54c1 | |||
| 02793a93f8 | |||
| db2e0bbcdd | |||
| 5918b5ed42 | |||
| 71d207a607 | |||
| 6ee4cd09f2 | |||
| 5de4de4b84 | |||
| e006722da7 | |||
| a52dc0cbf8 | |||
| 048ab0a8f4 | |||
| 261bb9e961 | |||
| 091f6179d4 | |||
| 1e1349cd80 | |||
| 14beb4f942 | |||
| 7ffcad64ba | |||
| 402fceb9f3 | |||
| 09b18e8ab8 | |||
| da72d03073 | |||
| a1a8d5f92a | |||
| b6dee4e46e | |||
| f3cd20fbf3 | |||
| da86c18205 | |||
| 8097e9b44a | |||
| 222852ff33 |
@@ -29,6 +29,7 @@ When using the Audio Transcription extension, you have the following options:
|
|||||||
- **Use Multilingual Model**: Enable this option to utilize the multilingual capabilities of OpenAI-whisper.
|
- **Use Multilingual Model**: Enable this option to utilize the multilingual capabilities of OpenAI-whisper.
|
||||||
- **Language**: Select the target language for transcription or translation. You can choose from a variety of languages supported by OpenAI-whisper.
|
- **Language**: Select the target language for transcription or translation. You can choose from a variety of languages supported by OpenAI-whisper.
|
||||||
- **Task:** Choose the specific task to perform on the audio. You can select either "transcribe" for transcription or "translate" to translate the audio to English.
|
- **Task:** Choose the specific task to perform on the audio. You can select either "transcribe" for transcription or "translate" to translate the audio to English.
|
||||||
|
- **Model Size**: Select the whisper model size to run the server with.
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
- Make sure the transcription server is running properly. To know more about how to start the server, see the [documentation here](https://github.com/collabora/whisper-live).
|
- Make sure the transcription server is running properly. To know more about how to start the server, see the [documentation here](https://github.com/collabora/whisper-live).
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ async function startCapture(options) {
|
|||||||
port: options.port,
|
port: options.port,
|
||||||
multilingual: options.useMultilingual,
|
multilingual: options.useMultilingual,
|
||||||
language: options.language,
|
language: options.language,
|
||||||
task: options.task
|
task: options.task,
|
||||||
|
modelSize: options.modelSize
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ async function startRecord(option) {
|
|||||||
uid: uuid,
|
uid: uuid,
|
||||||
multilingual: option.multilingual,
|
multilingual: option.multilingual,
|
||||||
language: option.language,
|
language: option.language,
|
||||||
task: option.task
|
task: option.task,
|
||||||
|
model_size: option.modelSize
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -125,11 +125,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dropdown-container">
|
<div class="dropdown-container">
|
||||||
<label for="taskDropdown">Select task:</label>
|
<label for="taskDropdown">Select task:</label>
|
||||||
<select id="taskDropdown" disabled>
|
<select id="taskDropdown" >
|
||||||
<option value="">Select Task</option>
|
<option value="">Select Task</option>
|
||||||
<option value="transcribe" selected>Transcribe</option>
|
<option value="transcribe" selected>Transcribe</option>
|
||||||
<option value="translate">Translate</option>
|
<option value="translate">Translate</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dropdown-container">
|
||||||
|
<label for="modelSizeDropdown">Select Model Size:</label>
|
||||||
|
<select id="modelSizeDropdown">
|
||||||
|
<option value="">Select Task</option>
|
||||||
|
<option value="tiny">Tiny</option>
|
||||||
|
<option value="base">Base</option>
|
||||||
|
<option value="small" selected>Small</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="large-v2">Large-v2</option>
|
||||||
|
<option value="large-v3">Large-v3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const useMultilingualCheckbox = document.getElementById('useMultilingualCheckbox');
|
const useMultilingualCheckbox = document.getElementById('useMultilingualCheckbox');
|
||||||
const languageDropdown = document.getElementById('languageDropdown');
|
const languageDropdown = document.getElementById('languageDropdown');
|
||||||
const taskDropdown = document.getElementById('taskDropdown');
|
const taskDropdown = document.getElementById('taskDropdown');
|
||||||
|
const modelSizeDropdown = document.getElementById('modelSizeDropdown');
|
||||||
let selectedLanguage = null;
|
let selectedLanguage = null;
|
||||||
let selectedTask = taskDropdown.value;
|
let selectedTask = taskDropdown.value;
|
||||||
|
let selectedModelSize = modelSizeDropdown.value;
|
||||||
|
|
||||||
// Add click event listeners to the buttons
|
// Add click event listeners to the buttons
|
||||||
startButton.addEventListener("click", startCapture);
|
startButton.addEventListener("click", startCapture);
|
||||||
@@ -52,6 +54,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
chrome.storage.local.get("selectedModelSize", ({ selectedModelSize: storedModelSize }) => {
|
||||||
|
if (storedModelSize !== undefined) {
|
||||||
|
modelSizeDropdown.value = storedModelSize;
|
||||||
|
selectedModelSize = storedModelSize;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Function to handle the start capture button click event
|
// Function to handle the start capture button click event
|
||||||
async function startCapture() {
|
async function startCapture() {
|
||||||
// Ignore click if the button is disabled
|
// Ignore click if the button is disabled
|
||||||
@@ -64,7 +73,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
// Send a message to the background script to start capturing
|
// Send a message to the background script to start capturing
|
||||||
let host = "localhost";
|
let host = "localhost";
|
||||||
let port = "9090";
|
let port = "5901";
|
||||||
const useCollaboraServer = useServerCheckbox.checked;
|
const useCollaboraServer = useServerCheckbox.checked;
|
||||||
if (useCollaboraServer){
|
if (useCollaboraServer){
|
||||||
host = "transcription.kurg.org"
|
host = "transcription.kurg.org"
|
||||||
@@ -79,7 +88,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
port: port,
|
port: port,
|
||||||
useMultilingual: useMultilingualCheckbox.checked,
|
useMultilingual: useMultilingualCheckbox.checked,
|
||||||
language: selectedLanguage,
|
language: selectedLanguage,
|
||||||
task: selectedTask
|
task: selectedTask,
|
||||||
|
modelSize: selectedModelSize
|
||||||
}, () => {
|
}, () => {
|
||||||
// Update capturing state in storage and toggle the buttons
|
// Update capturing state in storage and toggle the buttons
|
||||||
chrome.storage.local.set({ capturingState: { isCapturing: true } }, () => {
|
chrome.storage.local.set({ capturingState: { isCapturing: true } }, () => {
|
||||||
@@ -120,6 +130,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
stopButton.disabled = !isCapturing;
|
stopButton.disabled = !isCapturing;
|
||||||
useServerCheckbox.disabled = isCapturing;
|
useServerCheckbox.disabled = isCapturing;
|
||||||
useMultilingualCheckbox.disabled = isCapturing;
|
useMultilingualCheckbox.disabled = isCapturing;
|
||||||
|
modelSizeDropdown.disabled = isCapturing;
|
||||||
|
|
||||||
startButton.classList.toggle("disabled", isCapturing);
|
startButton.classList.toggle("disabled", isCapturing);
|
||||||
stopButton.classList.toggle("disabled", !isCapturing);
|
stopButton.classList.toggle("disabled", !isCapturing);
|
||||||
@@ -157,6 +168,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
chrome.storage.local.set({ selectedTask });
|
chrome.storage.local.set({ selectedTask });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelSizeDropdown.addEventListener('change', function() {
|
||||||
|
selectedModelSize = modelSizeDropdown.value;
|
||||||
|
chrome.storage.local.set({ selectedModelSize });
|
||||||
|
});
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
||||||
if (request.action === "updateSelectedLanguage") {
|
if (request.action === "updateSelectedLanguage") {
|
||||||
const detectedLanguage = request.detectedLanguage;
|
const detectedLanguage = request.detectedLanguage;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ When using the Audio Transcription extension, you have the following options:
|
|||||||
- **Use Multilingual Model**: Enable this option to utilize the multilingual capabilities of OpenAI-whisper.
|
- **Use Multilingual Model**: Enable this option to utilize the multilingual capabilities of OpenAI-whisper.
|
||||||
- **Language**: Select the target language for transcription or translation. You can choose from a variety of languages supported by OpenAI-whisper.
|
- **Language**: Select the target language for transcription or translation. You can choose from a variety of languages supported by OpenAI-whisper.
|
||||||
- **Task:** Choose the specific task to perform on the audio. You can select either "transcribe" for transcription or "translate" to translate the audio to English.
|
- **Task:** Choose the specific task to perform on the audio. You can select either "transcribe" for transcription or "translate" to translate the audio to English.
|
||||||
|
- **Model Size**: Select the whisper model size to run the server with.
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
- Make sure the transcription server is running properly. To know more about how to start the server, see the [documentation here](https://github.com/collabora/whisper-live).
|
- Make sure the transcription server is running properly. To know more about how to start the server, see the [documentation here](https://github.com/collabora/whisper-live).
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ function startRecording(data) {
|
|||||||
uid: uuid,
|
uid: uuid,
|
||||||
multilingual: data.useMultilingual,
|
multilingual: data.useMultilingual,
|
||||||
language: data.language,
|
language: data.language,
|
||||||
task: data.task
|
task: data.task,
|
||||||
|
model_size: data.modelSize
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -133,5 +133,17 @@
|
|||||||
<option value="translate">Translate</option>
|
<option value="translate">Translate</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="dropdown-container">
|
||||||
|
<label for="modelSizeDropdown">Select Model Size:</label>
|
||||||
|
<select id="modelSizeDropdown">
|
||||||
|
<option value="">Select Task</option>
|
||||||
|
<option value="tiny">Tiny</option>
|
||||||
|
<option value="base">Base</option>
|
||||||
|
<option value="small" selected>Small</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="large-v2">Large-v2</option>
|
||||||
|
<option value="large-v3">Large-v3</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -6,8 +6,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
const useMultilingualCheckbox = document.getElementById('useMultilingualCheckbox');
|
const useMultilingualCheckbox = document.getElementById('useMultilingualCheckbox');
|
||||||
const languageDropdown = document.getElementById('languageDropdown');
|
const languageDropdown = document.getElementById('languageDropdown');
|
||||||
const taskDropdown = document.getElementById('taskDropdown');
|
const taskDropdown = document.getElementById('taskDropdown');
|
||||||
|
const modelSizeDropdown = document.getElementById('modelSizeDropdown');
|
||||||
let selectedLanguage = null;
|
let selectedLanguage = null;
|
||||||
let selectedTask = taskDropdown.value;
|
let selectedTask = taskDropdown.value;
|
||||||
|
let selectedModelSize = modelSizeDropdown.value;
|
||||||
|
|
||||||
|
|
||||||
browser.storage.local.get("capturingState")
|
browser.storage.local.get("capturingState")
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
@@ -54,9 +57,16 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
browser.storage.local.get("selectedModelSize", ({ selectedModelSize: storedModelSize }) => {
|
||||||
|
if (storedModelSize !== undefined) {
|
||||||
|
modelSizeDropdown.value = storedModelSize;
|
||||||
|
selectedModelSize = storedModelSize;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
startButton.addEventListener("click", function() {
|
startButton.addEventListener("click", function() {
|
||||||
let host = "localhost";
|
let host = "localhost";
|
||||||
let port = "9090";
|
let port = "5901";
|
||||||
const useCollaboraServer = useServerCheckbox.checked;
|
const useCollaboraServer = useServerCheckbox.checked;
|
||||||
|
|
||||||
if (useCollaboraServer){
|
if (useCollaboraServer){
|
||||||
@@ -75,7 +85,8 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
port: port,
|
port: port,
|
||||||
useMultilingual: useMultilingualCheckbox.checked,
|
useMultilingual: useMultilingualCheckbox.checked,
|
||||||
language: selectedLanguage,
|
language: selectedLanguage,
|
||||||
task: selectedTask
|
task: selectedTask,
|
||||||
|
modelSize: selectedModelSize
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
toggleCaptureButtons(true);
|
toggleCaptureButtons(true);
|
||||||
@@ -115,6 +126,7 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
stopButton.disabled = !isCapturing;
|
stopButton.disabled = !isCapturing;
|
||||||
useServerCheckbox.disabled = isCapturing;
|
useServerCheckbox.disabled = isCapturing;
|
||||||
useMultilingualCheckbox.disabled = isCapturing;
|
useMultilingualCheckbox.disabled = isCapturing;
|
||||||
|
modelSizeDropdown.disabled = isCapturing;
|
||||||
|
|
||||||
startButton.classList.toggle("disabled", isCapturing);
|
startButton.classList.toggle("disabled", isCapturing);
|
||||||
stopButton.classList.toggle("disabled", !isCapturing);
|
stopButton.classList.toggle("disabled", !isCapturing);
|
||||||
@@ -152,6 +164,11 @@ document.addEventListener("DOMContentLoaded", function() {
|
|||||||
browser.storage.local.set({ selectedTask });
|
browser.storage.local.set({ selectedTask });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelSizeDropdown.addEventListener('change', function() {
|
||||||
|
selectedModelSize = modelSizeDropdown.value;
|
||||||
|
browser.storage.local.set({ selectedModelSize });
|
||||||
|
});
|
||||||
|
|
||||||
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
if (request.action === "updateSelectedLanguage") {
|
if (request.action === "updateSelectedLanguage") {
|
||||||
const detectedLanguage = request.data;
|
const detectedLanguage = request.data;
|
||||||
|
|||||||
@@ -28,19 +28,40 @@ Unlike traditional speech recognition systems that rely on continuous audio stre
|
|||||||
- To transcribe an audio file:
|
- To transcribe an audio file:
|
||||||
```python
|
```python
|
||||||
from whisper_live.client import TranscriptionClient
|
from whisper_live.client import TranscriptionClient
|
||||||
client = TranscriptionClient("localhost", 9090, is_multilingual=True, lang="hi", translate=True)
|
client = TranscriptionClient(
|
||||||
client(audio_file_path)
|
"localhost",
|
||||||
|
9090,
|
||||||
|
is_multilingual=False,
|
||||||
|
lang="en",
|
||||||
|
translate=False,
|
||||||
|
model_size="small"
|
||||||
|
)
|
||||||
|
|
||||||
|
client("tests/jfk.wav")
|
||||||
```
|
```
|
||||||
This command transcribes the specified audio file (audio.wav) using the Whisper model. It connects to the server running on localhost at port 9090. It also enables the multilingual feature, allowing transcription in multiple languages. The language option specifies the target language for transcription, in this case, Hindi ("hi"). The translate option should be set to `True` if we want to translate from the source language to English and `False` if we want to transcribe in the source language.
|
This command transcribes the specified audio file (audio.wav) using the Whisper model. It connects to the server running on localhost at port 9090. It can also enable the multilingual feature, allowing transcription in multiple languages. The language option specifies the target language for transcription, in this case, English ("en"). The translate option should be set to `True` if we want to translate from the source language to English and `False` if we want to transcribe in the source language.
|
||||||
|
|
||||||
- To transcribe from microphone:
|
- To transcribe from microphone:
|
||||||
```python
|
```python
|
||||||
from whisper_live.client import TranscriptionClient
|
from whisper_live.client import TranscriptionClient
|
||||||
client = TranscriptionClient(host, port, is_multilingual=True, lang="hi", translate=True)
|
client = TranscriptionClient(
|
||||||
|
"localhost",
|
||||||
|
9090,
|
||||||
|
is_multilingual=True,
|
||||||
|
lang="hi",
|
||||||
|
translate=True,
|
||||||
|
model_size="small"
|
||||||
|
)
|
||||||
client()
|
client()
|
||||||
```
|
```
|
||||||
This command captures audio from the microphone and sends it to the server for transcription. It uses the same options as the previous command, enabling the multilingual feature and specifying the target language and task.
|
This command captures audio from the microphone and sends it to the server for transcription. It uses the multilingual option with `hi` as the selected language, enabling the multilingual feature and specifying the target language and task. We use whisper `small` by default but can be changed to any other option based on the requirements and the hardware running the server.
|
||||||
|
|
||||||
|
- To transcribe from a HLS stream:
|
||||||
|
```python
|
||||||
|
client = TranscriptionClient(host, port, is_multilingual=True, lang="en", translate=False)
|
||||||
|
client(hls_url="http://as-hls-ww-live.akamaized.net/pool_904/live/ww/bbc_1xtra/bbc_1xtra.isml/bbc_1xtra-audio%3d96000.norewind.m3u8")
|
||||||
|
```
|
||||||
|
This command streams audio into the server from a HLS stream. It uses the same options as the previous command, enabling the multilingual feature and specifying the target language and task.
|
||||||
|
|
||||||
## Transcribe audio from browser
|
## Transcribe audio from browser
|
||||||
- Run the server
|
- Run the server
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
PyAudio
|
PyAudio
|
||||||
faster-whisper==0.9.0
|
faster-whisper==0.10.0
|
||||||
--extra-index-url https://download.pytorch.org/whl/cu111
|
--extra-index-url https://download.pytorch.org/whl/cu111
|
||||||
torch==1.10.1
|
torch==1.10.1
|
||||||
torchaudio==0.10.1
|
torchaudio==0.10.1
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ setup(name="whisper-live",
|
|||||||
),
|
),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"PyAudio",
|
"PyAudio",
|
||||||
"faster-whisper==0.6.0",
|
"faster-whisper==0.10.0",
|
||||||
"torch",
|
"torch",
|
||||||
"torchaudio",
|
"torchaudio",
|
||||||
"websockets",
|
"websockets",
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
__version__="0.0.8"
|
__version__="0.0.10"
|
||||||
+59
-13
@@ -50,7 +50,7 @@ class Client:
|
|||||||
INSTANCES = {}
|
INSTANCES = {}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, host=None, port=None, is_multilingual=False, lang=None, translate=False
|
self, host=None, port=None, is_multilingual=False, lang=None, translate=False, model_size="small"
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Initializes a Client instance for audio recording and streaming to a server.
|
Initializes a Client instance for audio recording and streaming to a server.
|
||||||
@@ -80,7 +80,9 @@ class Client:
|
|||||||
self.last_response_recieved = None
|
self.last_response_recieved = None
|
||||||
self.disconnect_if_no_response_for = 15
|
self.disconnect_if_no_response_for = 15
|
||||||
self.multilingual = is_multilingual
|
self.multilingual = is_multilingual
|
||||||
self.language = lang if is_multilingual else "en"
|
self.language = lang
|
||||||
|
self.model_size = model_size
|
||||||
|
self.server_error = False
|
||||||
if translate:
|
if translate:
|
||||||
self.task = "translate"
|
self.task = "translate"
|
||||||
|
|
||||||
@@ -140,11 +142,16 @@ class Client:
|
|||||||
print("[ERROR]: invalid client uid")
|
print("[ERROR]: invalid client uid")
|
||||||
return
|
return
|
||||||
|
|
||||||
if "status" in message.keys() and message["status"] == "WAIT":
|
if "status" in message.keys():
|
||||||
self.waiting = True
|
if message["status"] == "WAIT":
|
||||||
print(
|
self.waiting = True
|
||||||
f"[INFO]:Server is full. Estimated wait time {round(message['message'])} minutes."
|
print(
|
||||||
)
|
f"[INFO]:Server is full. Estimated wait time {round(message['message'])} minutes."
|
||||||
|
)
|
||||||
|
elif message["status"] == "ERROR":
|
||||||
|
print(f"Message from Server: {message['message']}")
|
||||||
|
self.server_error = True
|
||||||
|
return
|
||||||
|
|
||||||
if "message" in message.keys() and message["message"] == "DISCONNECT":
|
if "message" in message.keys() and message["message"] == "DISCONNECT":
|
||||||
print("[INFO]: Server overtime disconnected.")
|
print("[INFO]: Server overtime disconnected.")
|
||||||
@@ -213,6 +220,7 @@ class Client:
|
|||||||
"multilingual": self.multilingual,
|
"multilingual": self.multilingual,
|
||||||
"language": self.language,
|
"language": self.language,
|
||||||
"task": self.task,
|
"task": self.task,
|
||||||
|
"model_size": self.model_size,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -344,6 +352,42 @@ class Client:
|
|||||||
wavfile.setframerate(self.rate)
|
wavfile.setframerate(self.rate)
|
||||||
wavfile.writeframes(frames)
|
wavfile.writeframes(frames)
|
||||||
|
|
||||||
|
def process_hls_stream(self, hls_url):
|
||||||
|
"""
|
||||||
|
Connect to an HLS source, process the audio stream, and send it for transcription.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hls_url (str): The URL of the HLS stream source.
|
||||||
|
"""
|
||||||
|
print("[INFO]: Connecting to HLS stream...")
|
||||||
|
process = None # Initialize process to None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Connecting to the HLS stream using ffmpeg-python
|
||||||
|
process = (
|
||||||
|
ffmpeg
|
||||||
|
.input(hls_url, threads=0)
|
||||||
|
.output('-', format='s16le', acodec='pcm_s16le', ac=1, ar=self.rate)
|
||||||
|
.run_async(pipe_stdout=True, pipe_stderr=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process the stream
|
||||||
|
while True:
|
||||||
|
in_bytes = process.stdout.read(self.chunk * 2) # 2 bytes per sample
|
||||||
|
if not in_bytes:
|
||||||
|
break
|
||||||
|
audio_array = self.bytes_to_float_array(in_bytes)
|
||||||
|
self.send_packet_to_server(audio_array.tobytes())
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[ERROR]: Failed to connect to HLS stream: {e}")
|
||||||
|
finally:
|
||||||
|
if process:
|
||||||
|
process.kill()
|
||||||
|
|
||||||
|
print("[INFO]: HLS stream processing finished.")
|
||||||
|
|
||||||
|
|
||||||
def record(self, out_file="output_recording.wav"):
|
def record(self, out_file="output_recording.wav"):
|
||||||
"""
|
"""
|
||||||
Record audio data from the input stream and save it to a WAV file.
|
Record audio data from the input stream and save it to a WAV file.
|
||||||
@@ -461,10 +505,10 @@ class TranscriptionClient:
|
|||||||
transcription_client()
|
transcription_client()
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
def __init__(self, host, port, is_multilingual=False, lang=None, translate=False):
|
def __init__(self, host, port, is_multilingual=False, lang=None, translate=False, model_size="small"):
|
||||||
self.client = Client(host, port, is_multilingual, lang, translate)
|
self.client = Client(host, port, is_multilingual, lang, translate, model_size)
|
||||||
|
|
||||||
def __call__(self, audio=None):
|
def __call__(self, audio=None, hls_url=None):
|
||||||
"""
|
"""
|
||||||
Start the transcription process.
|
Start the transcription process.
|
||||||
|
|
||||||
@@ -478,12 +522,14 @@ class TranscriptionClient:
|
|||||||
"""
|
"""
|
||||||
print("[INFO]: Waiting for server ready ...")
|
print("[INFO]: Waiting for server ready ...")
|
||||||
while not self.client.recording:
|
while not self.client.recording:
|
||||||
if self.client.waiting:
|
if self.client.waiting or self.client.server_error:
|
||||||
self.client.close_websocket()
|
self.client.close_websocket()
|
||||||
return
|
return
|
||||||
pass
|
|
||||||
print("[INFO]: Server Ready!")
|
print("[INFO]: Server Ready!")
|
||||||
if audio is not None:
|
if hls_url is not None:
|
||||||
|
self.client.process_hls_stream(hls_url)
|
||||||
|
elif audio is not None:
|
||||||
resampled_file = resample(audio)
|
resampled_file = resample(audio)
|
||||||
self.client.play_file(resampled_file)
|
self.client.play_file(resampled_file)
|
||||||
else:
|
else:
|
||||||
|
|||||||
+49
-32
@@ -101,7 +101,8 @@ class TranscriptionServer:
|
|||||||
multilingual=options["multilingual"],
|
multilingual=options["multilingual"],
|
||||||
language=options["language"],
|
language=options["language"],
|
||||||
task=options["task"],
|
task=options["task"],
|
||||||
client_uid=options["uid"]
|
client_uid=options["uid"],
|
||||||
|
model_size=options["model_size"]
|
||||||
)
|
)
|
||||||
|
|
||||||
self.clients[websocket] = client
|
self.clients[websocket] = client
|
||||||
@@ -127,7 +128,8 @@ class TranscriptionServer:
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
self.clients[websocket].cleanup()
|
if self.clients[websocket].model_size is not None:
|
||||||
|
self.clients[websocket].cleanup()
|
||||||
self.clients.pop(websocket)
|
self.clients.pop(websocket)
|
||||||
self.clients_start_time.pop(websocket)
|
self.clients_start_time.pop(websocket)
|
||||||
logging.info("Connection Closed.")
|
logging.info("Connection Closed.")
|
||||||
@@ -180,7 +182,16 @@ class ServeClient:
|
|||||||
SERVER_READY = "SERVER_READY"
|
SERVER_READY = "SERVER_READY"
|
||||||
DISCONNECT = "DISCONNECT"
|
DISCONNECT = "DISCONNECT"
|
||||||
|
|
||||||
def __init__(self, websocket, task="transcribe", device=None, multilingual=False, language=None, client_uid=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
websocket,
|
||||||
|
task="transcribe",
|
||||||
|
device=None,
|
||||||
|
multilingual=False,
|
||||||
|
language=None,
|
||||||
|
client_uid=None,
|
||||||
|
model_size="small"
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Initialize a ServeClient instance.
|
Initialize a ServeClient instance.
|
||||||
The Whisper model is initialized based on the client's language and device availability.
|
The Whisper model is initialized based on the client's language and device availability.
|
||||||
@@ -199,11 +210,22 @@ class ServeClient:
|
|||||||
self.client_uid = client_uid
|
self.client_uid = client_uid
|
||||||
self.data = b""
|
self.data = b""
|
||||||
self.frames = b""
|
self.frames = b""
|
||||||
self.language = language if multilingual else "en"
|
self.model_sizes = [
|
||||||
|
"tiny", "base", "small", "medium", "large-v2", "large-v3"
|
||||||
|
]
|
||||||
|
self.multilingual = multilingual
|
||||||
|
self.model_size = self.get_model_size(model_size)
|
||||||
|
self.language = language if self.multilingual else "en"
|
||||||
self.task = task
|
self.task = task
|
||||||
|
self.websocket = websocket
|
||||||
|
|
||||||
device = "cuda" if torch.cuda.is_available() else "cpu"
|
device = "cuda" if torch.cuda.is_available() else "cpu"
|
||||||
|
|
||||||
|
if self.model_size == None:
|
||||||
|
return
|
||||||
|
|
||||||
self.transcriber = WhisperModel(
|
self.transcriber = WhisperModel(
|
||||||
"small" if multilingual else "small.en",
|
self.model_size,
|
||||||
device=device,
|
device=device,
|
||||||
compute_type="int8" if device=="cpu" else "float16",
|
compute_type="int8" if device=="cpu" else "float16",
|
||||||
local_files_only=False,
|
local_files_only=False,
|
||||||
@@ -228,7 +250,6 @@ class ServeClient:
|
|||||||
self.pick_previous_segments = 2
|
self.pick_previous_segments = 2
|
||||||
|
|
||||||
# threading
|
# threading
|
||||||
self.websocket = websocket
|
|
||||||
self.trans_thread = threading.Thread(target=self.speech_to_text)
|
self.trans_thread = threading.Thread(target=self.speech_to_text)
|
||||||
self.trans_thread.start()
|
self.trans_thread.start()
|
||||||
self.websocket.send(
|
self.websocket.send(
|
||||||
@@ -240,34 +261,30 @@ class ServeClient:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def fill_output(self, output):
|
def get_model_size(self, model_size):
|
||||||
"""
|
"""
|
||||||
Format the current incomplete transcription output by combining it with previous complete segments.
|
Returns the whisper model size based on multilingual.
|
||||||
The resulting transcription is wrapped into two lines, each containing a maximum of 50 characters.
|
|
||||||
|
|
||||||
It ensures that the combined transcription fits within two lines, with a maximum of 50 characters per line.
|
|
||||||
Segments are concatenated in the order they exist in the list of previous segments, with the most
|
|
||||||
recent complete segment first and older segments prepended as needed to maintain the character limit.
|
|
||||||
If a 3-second pause is detected in the previous segments, any text preceding it is discarded to ensure
|
|
||||||
the transcription starts with the most recent complete content. The resulting transcription is returned
|
|
||||||
as a single string.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output(str): The current incomplete transcription segment.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: A formatted transcription wrapped in two lines.
|
|
||||||
"""
|
"""
|
||||||
text = ''
|
if model_size not in self.model_sizes:
|
||||||
pick_prev = min(len(self.text), self.pick_previous_segments)
|
self.websocket.send(
|
||||||
for seg in self.text[-pick_prev:]:
|
json.dumps(
|
||||||
# discard everything before a 3 second pause
|
{
|
||||||
if seg == '':
|
"uid": self.client_uid,
|
||||||
text = ''
|
"status": "ERROR",
|
||||||
else:
|
"message": f"Invalid model size {model_size}. Available choices: {self.model_sizes}"
|
||||||
text += seg
|
}
|
||||||
wrapped = "".join(text + output)
|
)
|
||||||
return wrapped
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if model_size in ["large-v2", "large-v3"]:
|
||||||
|
self.multilingual = True
|
||||||
|
return model_size
|
||||||
|
|
||||||
|
if not self.multilingual:
|
||||||
|
model_size = model_size + ".en"
|
||||||
|
|
||||||
|
return model_size
|
||||||
|
|
||||||
def add_frames(self, frame_np):
|
def add_frames(self, frame_np):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import itertools
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import zlib
|
import zlib
|
||||||
|
import json
|
||||||
|
from inspect import signature
|
||||||
|
|
||||||
from typing import BinaryIO, Iterable, List, NamedTuple, Optional, Tuple, Union
|
from typing import BinaryIO, Iterable, List, NamedTuple, Optional, Tuple, Union
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ class WhisperModel:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
model_size_or_path: Size of the model to use (tiny, tiny.en, base, base.en,
|
model_size_or_path: Size of the model to use (tiny, tiny.en, base, base.en,
|
||||||
small, small.en, medium, medium.en, large-v1, large-v2, or large), a path to a converted
|
small, small.en, medium, medium.en, large-v1, large-v2, large-v3, or large), a path to a converted
|
||||||
model directory, or a CTranslate2-converted Whisper model ID from the Hugging Face Hub.
|
model directory, or a CTranslate2-converted Whisper model ID from the Hugging Face Hub.
|
||||||
When a size or a model ID is configured, the converted model is downloaded
|
When a size or a model ID is configured, the converted model is downloaded
|
||||||
from the Hugging Face Hub.
|
from the Hugging Face Hub.
|
||||||
@@ -144,7 +146,8 @@ class WhisperModel:
|
|||||||
"openai/whisper-tiny" + ("" if self.model.is_multilingual else ".en")
|
"openai/whisper-tiny" + ("" if self.model.is_multilingual else ".en")
|
||||||
)
|
)
|
||||||
|
|
||||||
self.feature_extractor = FeatureExtractor()
|
self.feat_kwargs = self._get_feature_kwargs(model_path)
|
||||||
|
self.feature_extractor = FeatureExtractor(**self.feat_kwargs)
|
||||||
self.num_samples_per_token = self.feature_extractor.hop_length * 2
|
self.num_samples_per_token = self.feature_extractor.hop_length * 2
|
||||||
self.frames_per_second = (
|
self.frames_per_second = (
|
||||||
self.feature_extractor.sampling_rate // self.feature_extractor.hop_length
|
self.feature_extractor.sampling_rate // self.feature_extractor.hop_length
|
||||||
@@ -161,6 +164,22 @@ class WhisperModel:
|
|||||||
"""The languages supported by the model."""
|
"""The languages supported by the model."""
|
||||||
return list(_LANGUAGE_CODES) if self.model.is_multilingual else ["en"]
|
return list(_LANGUAGE_CODES) if self.model.is_multilingual else ["en"]
|
||||||
|
|
||||||
|
def _get_feature_kwargs(self, model_path) -> dict:
|
||||||
|
preprocessor_config_file = os.path.join(model_path, "preprocessor_config.json")
|
||||||
|
config = {}
|
||||||
|
if os.path.isfile(preprocessor_config_file):
|
||||||
|
try:
|
||||||
|
with open(preprocessor_config_file, "r", encoding="utf-8") as json_file:
|
||||||
|
config = json.load(json_file)
|
||||||
|
valid_keys = signature(FeatureExtractor.__init__).parameters.keys()
|
||||||
|
config = {k: v for k, v in config.items() if k in valid_keys}
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
self.logger.warning(
|
||||||
|
"Could not load preprocessor_config.json: %s", str(e)
|
||||||
|
)
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
def transcribe(
|
def transcribe(
|
||||||
self,
|
self,
|
||||||
audio: Union[str, BinaryIO, np.ndarray],
|
audio: Union[str, BinaryIO, np.ndarray],
|
||||||
|
|||||||
Reference in New Issue
Block a user