duongthienz commited on
Commit
abd3d01
·
verified ·
1 Parent(s): b08e222

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -49
app.py CHANGED
@@ -54,14 +54,18 @@ def apply_speaker_renames_to_df(df, fileIndex, column="task"):
54
  df[column] = df[column].apply(lambda s: get_display_name(s, fileIndex))
55
  return df
56
 
57
- def extract_speaker_clip(audio_path, annotation, speaker, clip_duration=5):
58
  """
59
- Extract a clip of up to clip_duration seconds for the given speaker.
60
- Returns (bytes, None) on success, or (None, error_string) on failure.
 
61
  """
62
- import io, traceback
63
  try:
64
- # --- Step 1: collect segments for this speaker ---
 
 
 
65
  speaker_segments = []
66
  try:
67
  for seg, _, label in annotation.itertracks(yield_label=True):
@@ -71,9 +75,9 @@ def extract_speaker_clip(audio_path, annotation, speaker, clip_duration=5):
71
  return None, f"itertracks failed: {e}"
72
 
73
  if not speaker_segments:
74
- return None, f"No segments found for speaker '{speaker}' in annotation"
75
 
76
- # --- Step 2: pick best segment ---
77
  chosen_start, chosen_end = None, None
78
  for start, end in speaker_segments:
79
  if (end - start) >= clip_duration:
@@ -83,45 +87,20 @@ def extract_speaker_clip(audio_path, annotation, speaker, clip_duration=5):
83
  longest = max(speaker_segments, key=lambda s: s[1] - s[0])
84
  chosen_start, chosen_end = longest
85
 
86
- # --- Step 3: load audio with torchaudio, fallback to soundfile ---
87
- waveform, sample_rate = None, None
88
- load_errors = []
89
- try:
90
- waveform, sample_rate = torchaudio.load(audio_path)
91
- except Exception as e:
92
- load_errors.append(f"torchaudio.load: {e}")
93
-
94
- if waveform is None:
95
- try:
96
- import soundfile as sf
97
- import numpy as np
98
- data, sample_rate = sf.read(audio_path, dtype='float32')
99
- if data.ndim == 1:
100
- data = data[np.newaxis, :]
101
- else:
102
- data = data.T
103
- waveform = torch.from_numpy(data)
104
- except Exception as e:
105
- load_errors.append(f"soundfile: {e}")
106
-
107
- if waveform is None:
108
- return None, "Could not load audio: " + " | ".join(load_errors)
109
-
110
- # --- Step 4: slice and encode ---
111
  start_frame = int(chosen_start * sample_rate)
112
- end_frame = min(int(chosen_end * sample_rate), waveform.shape[-1])
113
  clip = waveform[:, start_frame:end_frame]
114
 
115
- buffer = io.BytesIO()
116
- torchaudio.save(buffer, clip, sample_rate, format="wav")
117
- buffer.seek(0)
118
- return buffer.read(), None
 
119
 
120
  except Exception as e:
121
- msg = traceback.format_exc()
122
- print(f"extract_speaker_clip unexpected error for {speaker}: {msg}")
123
  return None, str(e)
124
-
125
  @st.cache_data
126
  def convert_df(df):
127
  return df.to_csv(index=False).encode('utf-8')
@@ -182,7 +161,7 @@ def processFile(filePath):
182
  print("Speakers Detected")
183
  totalTimeInSeconds = int(waveform_gain_adjusted.shape[-1]/sampleRate)
184
  print("Time in seconds calculated")
185
- return annotations, totalTimeInSeconds
186
 
187
  def addCategory():
188
  newCategory = st.session_state.categoryInput
@@ -209,7 +188,7 @@ def updateCategoryOptions(resultIndex):
209
  #st.info(f"Updating result {resultIndex}")
210
  #st.info(f"In update: {st.session_state.categorySelect}")
211
  # Handle
212
- _, currAnnotation, _ = st.session_state.results[currFileIndex]
213
  speakerNames = currAnnotation.labels()
214
 
215
  # Handle speaker category sidebars
@@ -242,7 +221,7 @@ def analyze(inFileName):
242
 
243
  printV(f'In if',4)
244
  # Handle
245
- currAnnotation, currTotalTime = st.session_state.results[currFileIndex]
246
  speakerNames = currAnnotation.labels()
247
  printV(f'Loaded results',4)
248
  # Update other categories
@@ -567,9 +546,9 @@ else:
567
  st.session_state.unusedSpeakers[i] = speakerNames
568
  else:
569
  with st.spinner(text=f'Processing File {i+1} of {totalFiles}'):
570
- annotations, totalSeconds = processFile(file_paths[i])
571
  print(f"Finished processing {file_paths[i]}")
572
- st.session_state.results[i] = (annotations, totalSeconds)
573
  print("Results saved")
574
  st.session_state.summaries[i] = {}
575
  print("Summaries saved")
@@ -652,7 +631,10 @@ try:
652
  graphNames = ["Data","Voice Categories","Speaker Percentage","Speakers with Categories","Treemap","Timeline","Time Spoken"]
653
  dataTab, pie1, pie2, sunburst1, treemap1, timeline, bar1 = st.tabs(graphNames)
654
  # Handle
655
- currAnnotation, currTotalTime = st.session_state.results[currFileIndex]
 
 
 
656
  speakerNames = currAnnotation.labels()
657
 
658
  speakers_dataFrame = st.session_state.summaries[currFileIndex]["speakers_dataFrame"]
@@ -692,14 +674,14 @@ try:
692
  _display = get_display_name(sp, currFileIndex)
693
  st.sidebar.markdown(f"**{_display}**")
694
  _clip_bytes, _clip_err = extract_speaker_clip(
695
- _curr_audio_path, currAnnotation, sp, clip_duration=5
696
  )
697
  if _clip_bytes:
698
  st.sidebar.audio(_clip_bytes, format="audio/wav")
699
  else:
700
  st.sidebar.error(f"Clip error: {_clip_err}")
701
  else:
702
- st.sidebar.warning(f"Audio file not found at path: {_curr_audio_path}")
703
 
704
  st.sidebar.divider()
705
  st.sidebar.subheader("Rename Speakers")
@@ -1120,7 +1102,7 @@ if len(st.session_state.results) > 0:
1120
  }
1121
  allCategories = copy.deepcopy(st.session_state.categories)
1122
  for i in indices:
1123
- currAnnotation, currTotalTime = st.session_state.results[i]
1124
  categorySelections = st.session_state["categorySelect"][i]
1125
  catSummary,extraCats = su.calcCategories(currAnnotation,categorySelections)
1126
  st.session_state.summaries[i]["categories"] = (catSummary,extraCats)
 
54
  df[column] = df[column].apply(lambda s: get_display_name(s, fileIndex))
55
  return df
56
 
57
+ def extract_speaker_clip(annotation, speaker, waveform, sample_rate, clip_duration=5):
58
  """
59
+ Slice a clip directly from the already-loaded waveform tensor.
60
+ Avoids torchaudio.load (broken in this env due to torchcodec/libnppicc).
61
+ Returns (wav_bytes, None) on success or (None, error_str) on failure.
62
  """
63
+ import io, traceback as tb
64
  try:
65
+ if waveform is None or sample_rate is None:
66
+ return None, "No waveform stored (only audio uploads carry waveform data)"
67
+
68
+ # Collect this speaker's segments
69
  speaker_segments = []
70
  try:
71
  for seg, _, label in annotation.itertracks(yield_label=True):
 
75
  return None, f"itertracks failed: {e}"
76
 
77
  if not speaker_segments:
78
+ return None, f"No segments found for speaker '{speaker}'"
79
 
80
+ # Prefer first segment >= clip_duration; else take longest
81
  chosen_start, chosen_end = None, None
82
  for start, end in speaker_segments:
83
  if (end - start) >= clip_duration:
 
87
  longest = max(speaker_segments, key=lambda s: s[1] - s[0])
88
  chosen_start, chosen_end = longest
89
 
90
+ # Slice waveform
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  start_frame = int(chosen_start * sample_rate)
92
+ end_frame = min(int(chosen_end * sample_rate), waveform.shape[-1])
93
  clip = waveform[:, start_frame:end_frame]
94
 
95
+ # Encode to WAV bytes
96
+ buf = io.BytesIO()
97
+ torchaudio.save(buf, clip.cpu(), sample_rate, format="wav")
98
+ buf.seek(0)
99
+ return buf.read(), None
100
 
101
  except Exception as e:
102
+ print(f"extract_speaker_clip error for {speaker}: {tb.format_exc()}")
 
103
  return None, str(e)
 
104
  @st.cache_data
105
  def convert_df(df):
106
  return df.to_csv(index=False).encode('utf-8')
 
161
  print("Speakers Detected")
162
  totalTimeInSeconds = int(waveform_gain_adjusted.shape[-1]/sampleRate)
163
  print("Time in seconds calculated")
164
+ return annotations, totalTimeInSeconds, waveform_gain_adjusted, sampleRate
165
 
166
  def addCategory():
167
  newCategory = st.session_state.categoryInput
 
188
  #st.info(f"Updating result {resultIndex}")
189
  #st.info(f"In update: {st.session_state.categorySelect}")
190
  # Handle
191
+ _r = st.session_state.results[currFileIndex]; currAnnotation = _r[0]
192
  speakerNames = currAnnotation.labels()
193
 
194
  # Handle speaker category sidebars
 
221
 
222
  printV(f'In if',4)
223
  # Handle
224
+ _r = st.session_state.results[currFileIndex]; currAnnotation, currTotalTime = _r[0], _r[1]
225
  speakerNames = currAnnotation.labels()
226
  printV(f'Loaded results',4)
227
  # Update other categories
 
546
  st.session_state.unusedSpeakers[i] = speakerNames
547
  else:
548
  with st.spinner(text=f'Processing File {i+1} of {totalFiles}'):
549
+ annotations, totalSeconds, wf_stored, sr_stored = processFile(file_paths[i])
550
  print(f"Finished processing {file_paths[i]}")
551
+ st.session_state.results[i] = (annotations, totalSeconds, wf_stored, sr_stored)
552
  print("Results saved")
553
  st.session_state.summaries[i] = {}
554
  print("Summaries saved")
 
631
  graphNames = ["Data","Voice Categories","Speaker Percentage","Speakers with Categories","Treemap","Timeline","Time Spoken"]
632
  dataTab, pie1, pie2, sunburst1, treemap1, timeline, bar1 = st.tabs(graphNames)
633
  # Handle
634
+ _r = st.session_state.results[currFileIndex]
635
+ currAnnotation, currTotalTime = _r[0], _r[1]
636
+ currWaveform = _r[2] if len(_r) > 2 else None
637
+ currSampleRate = _r[3] if len(_r) > 3 else None
638
  speakerNames = currAnnotation.labels()
639
 
640
  speakers_dataFrame = st.session_state.summaries[currFileIndex]["speakers_dataFrame"]
 
674
  _display = get_display_name(sp, currFileIndex)
675
  st.sidebar.markdown(f"**{_display}**")
676
  _clip_bytes, _clip_err = extract_speaker_clip(
677
+ currAnnotation, sp, currWaveform, currSampleRate, clip_duration=5
678
  )
679
  if _clip_bytes:
680
  st.sidebar.audio(_clip_bytes, format="audio/wav")
681
  else:
682
  st.sidebar.error(f"Clip error: {_clip_err}")
683
  else:
684
+ st.sidebar.warning(f"Audio path not found: {_curr_audio_path}")
685
 
686
  st.sidebar.divider()
687
  st.sidebar.subheader("Rename Speakers")
 
1102
  }
1103
  allCategories = copy.deepcopy(st.session_state.categories)
1104
  for i in indices:
1105
+ _ri = st.session_state.results[i]; currAnnotation, currTotalTime = _ri[0], _ri[1]
1106
  categorySelections = st.session_state["categorySelect"][i]
1107
  catSummary,extraCats = su.calcCategories(currAnnotation,categorySelections)
1108
  st.session_state.summaries[i]["categories"] = (catSummary,extraCats)