Skip to content
Closed
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ Send from client to restart transcription after timeout or final message:
}
```

#### Pause / Resume Transcription
Temporarily halt or continue audio processing without closing the WebSocket:
```json
{ "type": "pause" }
```
Resume with:
```json
{ "type": "resume" }
```

### Usage Pattern

1. Connect to WebSocket endpoint
Expand Down
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ pub enum WebSocketMessage {
pub enum WebSocketCommand {
#[serde(rename = "restart")]
Restart,
#[serde(rename = "pause")]
Pause,
#[serde(rename = "resume")]
Resume,
}

pub struct TranscriptionOptions {
Expand Down Expand Up @@ -425,15 +429,21 @@ impl Model {
let (restart_tx, mut restart_rx) = mpsc::unbounded_channel();
let restart_tx = Arc::new(restart_tx);

// Channel for pause/resume commands
let (pause_tx, mut pause_rx) = mpsc::unbounded_channel::<bool>();
let pause_tx = Arc::new(pause_tx);

// Start WebSocket server
let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", ws_port)).await?;
let ws_tx_clone = ws_tx.clone();
let restart_tx_clone = restart_tx.clone();
let pause_tx_clone = pause_tx.clone();

tokio::spawn(async move {
while let Ok((stream, _)) = listener.accept().await {
let ws_tx = ws_tx_clone.clone();
let restart_tx = restart_tx_clone.clone();
let pause_tx = pause_tx_clone.clone();
tokio::spawn(async move {
let ws_stream = match accept_async(stream).await {
Ok(ws) => ws,
Expand All @@ -457,6 +467,12 @@ impl Model {
WebSocketCommand::Restart => {
let _ = restart_tx.send(());
}
WebSocketCommand::Pause => {
let _ = pause_tx.send(true);
}
WebSocketCommand::Resume => {
let _ = pause_tx.send(false);
}
}
}
}
Expand Down Expand Up @@ -499,10 +515,12 @@ impl Model {
let mut printed_eot = false;
let mut last_voice_activity: Option<std::time::Instant> = None;
let mut transcription_active = true;
let mut paused = false;

eprintln!("Starting transcription session...");

while transcription_active {
while let Ok(p) = pause_rx.try_recv() { paused = p; }
tokio::select! {
// Handle restart commands
_ = restart_rx.recv() => {
Expand All @@ -517,6 +535,9 @@ impl Model {
if save_audio.is_some() {
all_audio.extend_from_slice(&pcm_chunk);
}
if paused {
continue;
}

let mut has_voice_activity = false;

Expand Down
26 changes: 25 additions & 1 deletion websocket_example.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ <h1>eaRS WebSocket Transcription Client</h1>
<input type="text" id="wsUrl" value="ws://localhost:8080/" size="30">
<button onclick="connect()">Connect</button>
<button onclick="disconnect()">Disconnect</button>
<button id="pauseBtn" onclick="togglePause()" disabled>Pause</button>
</div>

<div>Status: <span id="status">Disconnected</span></div>
Expand All @@ -49,13 +50,16 @@ <h3>Instructions:</h3>
<li>Start eaRS with WebSocket: <code>ears --live --ws 8080</code></li>
<li>Click "Connect" above</li>
<li>Start speaking - words will appear in real-time</li>
<li>Use the Pause button to temporarily halt transcription</li>
</ol>
</div>

<script>
let ws = null;
let paused = false;
const messagesDiv = document.getElementById('messages');
const statusSpan = document.getElementById('status');
const pauseBtn = document.getElementById('pauseBtn');

function connect() {
const url = document.getElementById('wsUrl').value;
Expand All @@ -71,6 +75,7 @@ <h3>Instructions:</h3>
statusSpan.textContent = 'Connected';
statusSpan.style.color = 'green';
addMessage('Connected to eaRS WebSocket server', 'info');
pauseBtn.disabled = false;
};

ws.onmessage = function(event) {
Expand All @@ -87,6 +92,9 @@ <h3>Instructions:</h3>
statusSpan.style.color = 'red';
addMessage('Disconnected from server', 'info');
ws = null;
pauseBtn.disabled = true;
pauseBtn.textContent = 'Pause';
paused = false;
};

ws.onerror = function(error) {
Expand All @@ -102,6 +110,22 @@ <h3>Instructions:</h3>
if (ws) {
ws.close();
ws = null;
pauseBtn.disabled = true;
pauseBtn.textContent = 'Pause';
paused = false;
}
}

function togglePause() {
if (!ws) return;
if (paused) {
ws.send(JSON.stringify({ type: 'resume' }));
pauseBtn.textContent = 'Pause';
paused = false;
} else {
ws.send(JSON.stringify({ type: 'pause' }));
pauseBtn.textContent = 'Resume';
paused = true;
}
}

Expand Down Expand Up @@ -136,4 +160,4 @@ <h3>Instructions:</h3>
}
</script>
</body>
</html>
</html>