Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 15 additions & 14 deletions speech-to-text/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,24 @@ Convert audio to text using Smallest AI's Pulse Speech-to-Text API. Supports 30+

## Examples

| Example | Description |
|---------|-------------|
| [Getting Started](./getting-started/) | Basic transcription - the simplest way to get started |
| [Word-Level Outputs](./word-level-outputs/) | Word timestamps and speaker diarization |
| [Subtitle Generation](./subtitle-generation/) | Generate SRT/VTT subtitles from audio or video |
| [Meeting Notes](./meeting-notes/) | Join meetings via Recall.ai, auto-identify speakers by name |
| [Podcast Summarizer](./podcast-summarizer/) | Transcribe and summarize podcasts with GPT-4o |
| [File Transcription](./file-transcription/) | Transcribe files with all advanced features |
| [Emotion Analyzer](./emotion-analyzer/) | Visualize speaker emotions across a conversation with interactive charts |
| Example | Description |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| [Getting Started](./getting-started/) | Basic transcription - the simplest way to get started |
| [Word-Level Outputs](./word-level-outputs/) | Word timestamps and speaker diarization |
| [Subtitle Generation](./subtitle-generation/) | Generate SRT/VTT subtitles from audio or video |
| [Multilingual Live Captions](./websocket/multilingual-live-captions/) | Auto-detect language, translate, and preview live SRT captions |
| [Meeting Notes](./meeting-notes/) | Join meetings via Recall.ai, auto-identify speakers by name |
| [Podcast Summarizer](./podcast-summarizer/) | Transcribe and summarize podcasts with GPT-4o |
| [File Transcription](./file-transcription/) | Transcribe files with all advanced features |
| [Emotion Analyzer](./emotion-analyzer/) | Visualize speaker emotions across a conversation with interactive charts |

### WebSocket Examples

| Example | Description |
|---------|-------------|
| [Streaming Transcription](./websocket/streaming-text-output-transcription/) | Stream audio files via WebSocket |
| [Realtime Microphone](./websocket/realtime-microphone-transcription/) | Gradio web UI with live microphone transcription |
| [Jarvis Voice Assistant](./websocket/jarvis/) | Always-on assistant with wake word, LLM, and TTS |
| Example | Description |
| --------------------------------------------------------------------------- | ------------------------------------------------ |
| [Streaming Transcription](./websocket/streaming-text-output-transcription/) | Stream audio files via WebSocket |
| [Realtime Microphone](./websocket/realtime-microphone-transcription/) | Gradio web UI with live microphone transcription |
| [Jarvis Voice Assistant](./websocket/jarvis/) | Always-on assistant with wake word, LLM, and TTS |

## Quick Start

Expand Down
49 changes: 4 additions & 45 deletions speech-to-text/subtitle-generation/python/transcribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
import requests
from dotenv import load_dotenv

sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from subtitle_utils import format_timestamp_srt, format_timestamp_vtt, generate_srt, generate_vtt

load_dotenv()

API_URL = "https://waves-api.smallest.ai/api/v1/pulse/get_text"
API_URL = "https://api.smallest.ai/waves/v1/pulse/get_text"

LANGUAGE = "en" # Use ISO 639-1 codes or "multi" for auto-detect
WORDS_PER_SEGMENT = 10 # Maximum words per subtitle segment
Expand Down Expand Up @@ -52,24 +55,6 @@ def transcribe(audio_file: str, api_key: str) -> dict:
return response.json()


def format_time_srt(seconds: float) -> str:
"""Format seconds to SRT timestamp: HH:MM:SS,mmm"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
millis = int((seconds % 1) * 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"


def format_time_vtt(seconds: float) -> str:
"""Format seconds to VTT timestamp: HH:MM:SS.mmm"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
millis = int((seconds % 1) * 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d}.{millis:03d}"


def create_segments(words: list) -> list:
"""Group words into subtitle segments."""
segments = []
Expand Down Expand Up @@ -103,32 +88,6 @@ def create_segments(words: list) -> list:
return segments


def generate_srt(segments: list) -> str:
"""Generate SRT format subtitles."""
lines = []
for i, segment in enumerate(segments, 1):
start = format_time_srt(segment["start"])
end = format_time_srt(segment["end"])
lines.append(f"{i}")
lines.append(f"{start} --> {end}")
lines.append(segment["text"])
lines.append("")
return "\n".join(lines)


def generate_vtt(segments: list) -> str:
"""Generate WebVTT format subtitles."""
lines = ["WEBVTT", ""]
for i, segment in enumerate(segments, 1):
start = format_time_vtt(segment["start"])
end = format_time_vtt(segment["end"])
lines.append(f"{i}")
lines.append(f"{start} --> {end}")
lines.append(segment["text"])
lines.append("")
return "\n".join(lines)


def process_response(result: dict, audio_path: Path):
if result.get("status") != "success":
print("Error: Transcription failed")
Expand Down
152 changes: 152 additions & 0 deletions speech-to-text/subtitle_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""
Shared subtitle utilities for Smallest AI Speech-to-Text applications.

This module provides reusable functions for generating SRT and VTT subtitle
formats with proper timestamp formatting.
"""

from typing import List, Dict, Callable, Optional


def format_timestamp_srt(seconds: float) -> str:
Comment thread
hemant838 marked this conversation as resolved.
"""
Format seconds to SRT timestamp format: HH:MM:SS,mmm

Args:
seconds: Time in seconds (can be float)

Returns:
Formatted timestamp string in SRT format

Example:
>>> format_timestamp_srt(65.123)
'00:01:05,123'
"""
ms = int(seconds * 1000)
hours, ms = divmod(ms, 3600 * 1000)
minutes, ms = divmod(ms, 60 * 1000)
secs, ms = divmod(ms, 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d},{ms:03d}"
Comment on lines +11 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate Code: ⚠️ Duplicate Code Detected (Similarity: 95%)

This function format_timestamp_srt duplicates existing code.

📍 Original Location:

speech-to-text/subtitle-generation/javascript/transcribe.js:48-54

Function: formatTimeSrt

💡 Recommendation:
Since these are in different languages, full consolidation isn't possible, but the Python version in subtitle_utils.py should be treated as the canonical implementation for Python consumers. The JS version in transcribe.js should remain separate. Document that both implement the same spec so they stay in sync.

Consider importing and reusing the existing function instead of duplicating the logic.



def format_timestamp_vtt(seconds: float) -> str:
Comment thread
hemant838 marked this conversation as resolved.
"""
Format seconds to VTT timestamp format: HH:MM:SS.mmm

Args:
seconds: Time in seconds (can be float)

Returns:
Formatted timestamp string in VTT format

Example:
>>> format_timestamp_vtt(65.123)
'00:01:05.123'
"""
ms = int(seconds * 1000)
hours, ms = divmod(ms, 3600 * 1000)
minutes, ms = divmod(ms, 60 * 1000)
secs, ms = divmod(ms, 1000)
return f"{hours:02d}:{minutes:02d}:{secs:02d}.{ms:03d}"
Comment on lines +32 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate Code: ⚠️ Duplicate Code Detected (Similarity: 95%)

This function format_timestamp_vtt duplicates existing code.

📍 Original Location:

speech-to-text/subtitle-generation/javascript/transcribe.js:56-62

Function: formatTimeVtt

💡 Recommendation:
Same as format_timestamp_srt: treat subtitle_utils.py as the canonical Python implementation. Add cross-language documentation noting both versions must use . as separator.

Consider importing and reusing the existing function instead of duplicating the logic.



def generate_srt(
Comment thread
hemant838 marked this conversation as resolved.
entries: List[Dict],
text_key: str = "text",
start_key: str = "start",
end_key: str = "end",
format_func: Optional[Callable[[float], str]] = None
) -> str:
"""
Generate SRT format subtitles from a list of entries.

Args:
entries: List of dictionaries containing subtitle data
text_key: Key name for the text content in each entry
start_key: Key name for the start timestamp in each entry
end_key: Key name for the end timestamp in each entry
format_func: Optional custom timestamp formatting function.
Defaults to format_timestamp_srt.

Returns:
Complete SRT subtitle string

Example:
>>> entries = [
... {"text": "Hello", "start": 0.0, "end": 1.5},
... {"text": "World", "start": 1.5, "end": 3.0}
... ]
>>> print(generate_srt(entries))
1
00:00:00,000 --> 00:00:01,500
Hello

2
00:00:01,500 --> 00:00:03,000
World
"""
if format_func is None:
format_func = format_timestamp_srt

lines = []
for idx, entry in enumerate(entries, start=1):
start = format_func(entry[start_key])
end = format_func(entry[end_key])
lines.append(str(idx))
lines.append(f"{start} --> {end}")
lines.append(entry[text_key])
lines.append("")

return "\n".join(lines).strip()


def generate_vtt(
Comment thread
hemant838 marked this conversation as resolved.
entries: List[Dict],
text_key: str = "text",
start_key: str = "start",
end_key: str = "end",
format_func: Optional[Callable[[float], str]] = None
) -> str:
"""
Generate WebVTT format subtitles from a list of entries.

Args:
entries: List of dictionaries containing subtitle data
text_key: Key name for the text content in each entry
start_key: Key name for the start timestamp in each entry
end_key: Key name for the end timestamp in each entry
format_func: Optional custom timestamp formatting function.
Defaults to format_timestamp_vtt.

Returns:
Complete WebVTT subtitle string

Example:
>>> entries = [
... {"text": "Hello", "start": 0.0, "end": 1.5},
... {"text": "World", "start": 1.5, "end": 3.0}
... ]
>>> print(generate_vtt(entries))
WEBVTT

1
00:00:00.000 --> 00:00:01.500
Hello

2
00:00:01.500 --> 00:00:03.000
World
"""
if format_func is None:
format_func = format_timestamp_vtt

lines = ["WEBVTT", ""]
for idx, entry in enumerate(entries, start=1):
start = format_func(entry[start_key])
end = format_func(entry[end_key])
lines.append(str(idx))
lines.append(f"{start} --> {end}")
lines.append(entry[text_key])
lines.append("")

return "\n".join(lines).strip()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Smallest AI API Key
SMALLEST_API_KEY=your-smallest-api-key

# Optional: translation (falls back to source if unset)
OPENAI_API_KEY=your-openai-api-key
78 changes: 78 additions & 0 deletions speech-to-text/websocket/multilingual-live-captions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Multilingual Live Captions

Auto-detect spoken language, translate on the fly, and preview SRT captions for live events or streams.

## Features

- Automatic language detection (`language=multi`) with Pulse STT
- Live translation to a chosen subtitle language
- Instant SRT preview you can copy into players/overlays
- Works from your microphone via WebSocket streaming

## Requirements

> Base dependencies are installed via the root `requirements.txt`. See the [main README](../../../README.md#usage) for setup. Add `SMALLEST_API_KEY` to your `.env`.

Extra dependencies:

```bash
uv pip install -r requirements.txt
```

This installs `numpy` (used for lightweight resampling). Translation uses OpenAI if `OPENAI_API_KEY` is set; otherwise it falls back to source text.

## Usage

```bash
uv run app.py
```

Then open http://localhost:7860 and:

1. Choose subtitle language (e.g., English)
2. Click microphone and speak — transcript + translation update live
3. Copy SRT preview into your caption tool or player
4. Click **Stop & Clear** to reset

## Recommended Usage

- Live captions for multilingual events or webinars
- Streaming overlays that need translated subtitles quickly
- As a building block for hybrid ASR + translation caption services

## How It Works

- Streams mic audio to Pulse STT over WebSocket with `language=multi`
- Receives partial/final transcripts with detected language metadata
- Runs on-the-fly translation via OpenAI (if configured)
- Accumulates segments and renders an SRT preview for copy/paste

## Configuration

| Setting | Default | Description |
| ----------------- | --------- | ------------------------------------------------------------- |
| `language` | `multi` | Enables automatic language detection |
| Subtitle dropdown | `English` | Target translation language (`Same as source` keeps original) |
| Sample rate | 16000 Hz | Resampled client-side if needed |

## Example Output (SRT)

```
1
00:00:00,000 --> 00:00:03,400
Welcome to the live demo.

2
00:00:03,400 --> 00:00:06,200
Here are your translated captions.
```

## API Reference

- [Streaming Quickstart](https://waves-docs.smallest.ai/v4.0.0/content/speech-to-text-new/streaming/quickstart)
- [Pulse STT WebSocket API](https://waves-docs.smallest.ai/content/api-references/pulse-stt-ws)

## Next Steps

- [Realtime Microphone Transcription](../realtime-microphone-transcription/) — Basic live STT without translation
- [Jarvis Voice Assistant](../jarvis/) — Full assistant with wake word, LLM, and TTS
Loading