RemiFabre Claude Opus 4.6 commited on
Commit
ebf6e04
Β·
1 Parent(s): dbc544f

Fix recording sync, auto-fill name from WAV, refresh moves list

Browse files

- Apply AUDIO_LEAD_MS compensation during recording too: start uploaded
audio 320ms before capture so the user hears it in sync. Without this,
the recorded motion was 320ms ahead of the audio content, causing
desync on playback.
- Auto-fill recording name from uploaded WAV filename (upload + robot).
- Refresh moves list when mode transitions from busy to idle, so newly
recorded moves appear immediately without needing a page reload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

marionette/recording.py CHANGED
@@ -218,11 +218,20 @@ class RecordingMixin:
218
 
219
  # When using uploaded audio, don't record from mic (audio comes from the file)
220
  should_record_mic = request.record_audio and not request.uploaded_audio_path
 
 
 
 
 
 
 
 
 
 
221
  try:
222
  timestamps, frames, audio_frames, audio_samplerate = self._capture_motion(
223
  reachy_mini, stop_event, request.duration, should_record_mic,
224
  record_motion=request.record_motion,
225
- on_capture_start=(audio_start.set if audio_thread is not None else None),
226
  )
227
  finally:
228
  # Unblock the audio thread's start_signal.wait() first, then
 
218
 
219
  # When using uploaded audio, don't record from mic (audio comes from the file)
220
  should_record_mic = request.record_audio and not request.uploaded_audio_path
221
+
222
+ # Start uploaded audio BEFORE capture so the user hears it in sync.
223
+ # The same AUDIO_LEAD_MS compensation as playback: audio needs this
224
+ # head-start to traverse the GStreamer pipeline before reaching the
225
+ # speaker. Without this, the user hears the audio ~320ms late and
226
+ # their recorded motion ends up 320ms ahead of the audio content.
227
+ if audio_thread is not None:
228
+ audio_start.set()
229
+ time.sleep(AUDIO_LEAD_MS / 1000.0)
230
+
231
  try:
232
  timestamps, frames, audio_frames, audio_samplerate = self._capture_motion(
233
  reachy_mini, stop_event, request.duration, should_record_mic,
234
  record_motion=request.record_motion,
 
235
  )
236
  finally:
237
  # Unblock the audio thread's start_signal.wait() first, then
marionette/static/main.js CHANGED
@@ -203,8 +203,10 @@ function updateUI(s) {
203
  $modeBadge.dataset.mode = s.mode;
204
  }
205
 
206
- // ── Busy flag ──
 
207
  busy = !['idle', 'queued'].includes(s.mode);
 
208
 
209
  // ── Record buttons state ──
210
  const activeEntry = s.datasets?.entries?.find(e => e.id === s.datasets?.active_id);
@@ -825,6 +827,10 @@ async function uploadAudioFile(file) {
825
  if (data.duration && $recDuration) {
826
  $recDuration.value = (Math.round(data.duration * 10) / 10).toFixed(1);
827
  }
 
 
 
 
828
  } catch (e) {
829
  $audioUploadStatus.textContent = 'Failed: ' + e.message;
830
  uploadedAudioId = null;
@@ -890,6 +896,10 @@ async function selectRobotAudio() {
890
  if (data.duration && $recDuration) {
891
  $recDuration.value = (Math.round(data.duration * 10) / 10).toFixed(1);
892
  }
 
 
 
 
893
  } catch (e) {
894
  console.error('Robot audio select error:', e);
895
  }
 
203
  $modeBadge.dataset.mode = s.mode;
204
  }
205
 
206
+ // ── Busy flag + refresh moves after recording/playback completes ──
207
+ const wasBusy = busy;
208
  busy = !['idle', 'queued'].includes(s.mode);
209
+ if (wasBusy && !busy) movesListDirty = true;
210
 
211
  // ── Record buttons state ──
212
  const activeEntry = s.datasets?.entries?.find(e => e.id === s.datasets?.active_id);
 
827
  if (data.duration && $recDuration) {
828
  $recDuration.value = (Math.round(data.duration * 10) / 10).toFixed(1);
829
  }
830
+ // Auto-fill recording name from the audio filename (without extension)
831
+ if (!$recName.value.trim()) {
832
+ $recName.value = file.name.replace(/\.[^.]+$/, '');
833
+ }
834
  } catch (e) {
835
  $audioUploadStatus.textContent = 'Failed: ' + e.message;
836
  uploadedAudioId = null;
 
896
  if (data.duration && $recDuration) {
897
  $recDuration.value = (Math.round(data.duration * 10) / 10).toFixed(1);
898
  }
899
+ // Auto-fill recording name from the robot audio filename (without extension)
900
+ if (!$recName.value.trim() && data.filename) {
901
+ $recName.value = data.filename.replace(/\.[^.]+$/, '');
902
+ }
903
  } catch (e) {
904
  console.error('Robot audio select error:', e);
905
  }