Jahmori-R commited on
Commit
1535fc0
·
verified ·
1 Parent(s): a2ff65f

Sync from Jahmori-R/PureVoiceAI-HG via hub-sync

Browse files
.github/workflows/publish.yml DELETED
@@ -1,23 +0,0 @@
1
- name: Sync to Hugging Face hub
2
- on:
3
- push:
4
- branches: [main]
5
-
6
- # to run this workflow manually from the Actions tab
7
- workflow_dispatch:
8
-
9
- jobs:
10
- sync-to-hub:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/checkout@v2
14
- with:
15
- fetch-depth: 0
16
- - name: Add remote
17
- env:
18
- HF: ${{ secrets.HF }}
19
- run: git remote add space https://Jahmori-R:$HF@huggingface.co/spaces/Jahmori-R/PureVoiceAISpace
20
- - name: Push to hub
21
- env:
22
- HF: ${{ secrets.HF }}
23
- run: git push --force https://Jahmori-R:$HF@huggingface.co/spaces/Jahmori-R/PureVoiceAISpace main
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore DELETED
@@ -1,28 +0,0 @@
1
-
2
-
3
-
4
- # Virtual Environments
5
- .env
6
- .venv
7
- env/
8
- venv/
9
- ENV/
10
-
11
- #pyenv versions control
12
- .python-version
13
-
14
-
15
- # Ignore specific output directories
16
- uploads
17
- pretrained_models
18
- local
19
-
20
-
21
- # Audio formats from processing
22
- *.wav
23
- *.flac
24
- *.mp3
25
-
26
-
27
- # Configs
28
- pyrightconfig.json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -9,26 +9,24 @@ python_version: 3.12.12
9
  app_file: app.py
10
  pinned: false
11
  license: apache-2.0
12
- short_description: Space to demonstrate ai logging pipeline system
13
  ---
14
 
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
16
-
17
  # PureVoiceAI
18
 
19
- An advanced, end-to-end audio processing pipeline designed to improve the quality of transcription/note-taking. PureVoiceAI integrates machine learning models to perform audio source separation, voice activity detection, and speech transcription, turning noisy, overlapping audio into clean, attributed text.
20
 
21
  ## Key Features
22
 
23
- - **Advanced Source Separation:** Utilizes modern model **RE-SepFormer** to isolate individual voices from noisy or overlapping audio tracks.
24
- - **Speaker Diarization:** Accurately determines "who spoke when," ensuring that transcribed text is correctly attributed to individual speakers.
25
- - **Speech Transcription:** Converts the cleaned, separated audio streams into highly accurate text.
26
- - **Modern AI Tooling:** Built on top of robust, industry-standard audio processing toolkit [SpeechBrain](https://speechbrain.github.io/).
27
 
28
  ## Tech Stack
29
 
30
  - **Language:** Python
31
- - **Package Management:** `pip`
32
  - **Audio Frameworks:** SpeechBrain
33
  - **Core ML Models:** RE-SepFormer, pyAnnote, OpenAI Whisper
34
 
 
9
  app_file: app.py
10
  pinned: false
11
  license: apache-2.0
12
+ short_description: AI speech logging transcription pipeline
13
  ---
14
 
 
 
15
  # PureVoiceAI
16
 
17
+ An end-to-end audio processing pipeline designed to improve the quality of transcription by addressing the speech overlap problem. PureVoiceAI integrates machine learning models to perform audio source separation, voice activity detection, and speech transcription, turning noisy, overlapping audio into clean, attributed text.
18
 
19
  ## Key Features
20
 
21
+ - **Advanced Source Separation:** Utilizes custom trained model **RE-SepFormer** to isolate individual voices from noisy or overlapping audio tracks.
22
+ - **Voice Activity Detection:** Accurately determines "who spoke when," ensuring that transcribed text is correctly attributed to individual speakers.
23
+ - **Speech Transcription:** Converts the cleaned, separated audio streams into highly accurate text given voice activity timestamps.
24
+ - **Toolkits:** Built on top of audio processing toolkit [SpeechBrain](https://speechbrain.github.io/).
25
 
26
  ## Tech Stack
27
 
28
  - **Language:** Python
29
+ - **Package Management:** pip
30
  - **Audio Frameworks:** SpeechBrain
31
  - **Core ML Models:** RE-SepFormer, pyAnnote, OpenAI Whisper
32
 
app.py CHANGED
@@ -14,20 +14,22 @@ from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
14
  from speechbrain.inference.separation import SepformerSeparation
15
  from pydub import AudioSegment
16
 
17
- # Disable TF32 for maximum precision (fixes the Pyannote warning)
18
  torch.backends.cuda.matmul.allow_tf32 = False
19
  torch.backends.cudnn.allow_tf32 = False
20
 
21
- device = "cuda:0" if torch.cuda.is_available() else "cpu" # GPU availability check
22
 
23
-
24
- token = os.environ.get("HF_TOKEN")
25
  temp_dir = tempfile.gettempdir()
26
-
27
 
28
  @spaces.GPU
29
  def process_audio(mixture):
30
- print(f"temp directory created: {temp_dir}")
 
 
 
 
31
 
32
  # Name of separated file folder
33
  sources = Path(temp_dir) / "sources"
@@ -36,10 +38,12 @@ def process_audio(mixture):
36
  Path.mkdir(sources, exist_ok=True)
37
 
38
  # Downloads RE-SepFormer model from huggingface buckets to space local directory
39
- sync_bucket("hf://buckets/Jahmori-R/PureVoiceAISpace-storage", "./local")
 
 
40
 
41
  # ---------------------------------------------------------
42
- # INITIALIZE MODELS
43
  # ---------------------------------------------------------
44
 
45
  print("Intializing models...")
@@ -47,28 +51,22 @@ def process_audio(mixture):
47
  # Loading Resepformer Model
48
  try:
49
  separator = SepformerSeparation.from_hparams(
50
- source=f"./local/resepformer-librimix2spk",
51
  hparams_file="hyperparams.yaml",
52
- run_opts={"device": "cuda"},
53
  )
54
-
55
- # separator = SepformerSeparation.from_hparams(
56
- # source="Jahmori-R/resepformer-librimix2spk",
57
- # savedir=f"./pretrained_models/resepformer-librimix2spk",
58
- # # run_opts={"device": "cuda"},
59
- # # fetch_config=FetchConfig(token=True)
60
- # )
61
-
62
  print("RE-SepFormer model successfully loaded")
63
  except Exception as e:
64
  print(f"Failed to load RE-SepFormer model. Error: {e}")
65
  raise SystemExit()
66
 
67
- # #Loading Pyannote Model
 
68
  try:
69
  diarize = Pipeline.from_pretrained(
70
  "pyannote/speaker-diarization-community-1", token=token
71
  )
 
72
  diarize.to(torch.device("cuda"))
73
  print("Pyannote model successfully loaded")
74
  except Exception as e:
@@ -76,8 +74,7 @@ def process_audio(mixture):
76
  raise SystemExit()
77
 
78
  # Loading Whisper Model
79
- # Prevents WhisperAI logging in cmd
80
- transformers.logging.set_verbosity_error()
81
  try:
82
  transcription_model_id = "openai/whisper-large-v3-turbo"
83
  torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
@@ -108,15 +105,16 @@ def process_audio(mixture):
108
  # LOAD & SEPARATE AUDIO
109
  # ---------------------------------------------------------
110
 
111
- # Importing mixed audio source
112
 
113
- # Calling ReSepformer model onto audio source to separate
114
  assert separator is not None, "Failed to load the Sepformer model."
115
 
 
116
  print("Separating Audio Mixture")
117
  est_sources = separator.separate_file(mixture)
118
  print("Mixture separated")
119
 
 
120
  torchaudio.save(
121
  f"{sources}/speaker_1.wav", est_sources[:, :, 0].detach().cpu(), 16000
122
  )
@@ -125,17 +123,17 @@ def process_audio(mixture):
125
  )
126
 
127
  # ---------------------------------------------------------
128
- # DIARIZATION ON FILES
129
  # ---------------------------------------------------------
130
 
131
  # Dictionary to store speaker information
132
  unfiltered_log = [[], []]
133
- overlap_durations = []
134
 
135
  # Storing both speakers
136
  file_paths = sorted(
 
137
  glob.glob(f"{sources}/speaker_*.wav")
138
- ) # Glob finds all similar files
139
 
140
  spk_1 = file_paths[0]
141
  spk_2 = file_paths[1]
@@ -148,7 +146,7 @@ def process_audio(mixture):
148
  print(f"Diarizing {speaker_key}")
149
  diarization = diarize(path, num_speakers=1)
150
 
151
- # Storing speaker voice activity timestamp to dictionary
152
  for turn, _ in diarization.speaker_diarization:
153
  unfiltered_log[i].append(
154
  {
@@ -158,6 +156,8 @@ def process_audio(mixture):
158
  }
159
  )
160
 
 
 
161
  audio_seg = AudioSegment.from_file(path)
162
 
163
  print("Transcribing Chunks...")
@@ -168,10 +168,11 @@ def process_audio(mixture):
168
  end_ms = segment["end"] * 1000
169
  chunk = audio_seg[start_ms:end_ms]
170
 
171
- # Converting pydub AudioSegment to numpy array
172
  samples = np.array(chunk.get_array_of_samples())
173
  samples = samples.astype(np.float32) / 32768.0
174
 
 
175
  transcription = transcribe(
176
  samples,
177
  generate_kwargs={
@@ -179,6 +180,7 @@ def process_audio(mixture):
179
  "condition_on_prev_tokens": True,
180
  },
181
  )
 
182
  segment["transcription"] = transcription
183
 
184
  # Combined both speaker logs
@@ -187,26 +189,11 @@ def process_audio(mixture):
187
  # Sorting by start time of timestamps
188
  filtered_log = sorted(combined_log, key=lambda x: x["start"])
189
 
190
- # Calculating the overlap between each segment to assign appropriate speaker to segment
191
- # for log, log_next in zip(filtered_log, filtered_log[1:]):
192
- # current_start = log["start"]
193
- # current_end = log["end"]
194
-
195
- # next_start = log_next["start"]
196
- # next_end = log_next["end"]
197
-
198
- # overlap_start = max(current_start, next_start)
199
- # overlap_end = min(current_end, next_end)
200
- # overlap = max(0.0, overlap_end - overlap_start)
201
- # overlap_durations.append(overlap)
202
-
203
- # Overlkap formula = Min(end 1, end 2) - Max(start 1, start 2)
204
-
205
  print("Task Finished")
206
  return (spk_1, spk_2, filtered_log)
207
 
208
 
209
- # css for interface modification
210
  css = """
211
 
212
  #centered-row {
@@ -215,6 +202,7 @@ css = """
215
  }
216
  """
217
 
 
218
  with gr.Blocks(css=css) as demo:
219
  gr.Markdown("PureVoiceAI")
220
  with gr.Row(variant="panel"):
@@ -242,4 +230,4 @@ with gr.Blocks(css=css) as demo:
242
  )
243
 
244
  if __name__ == "__main__":
245
- demo.launch(allowed_paths=[temp_dir], theme=gr.themes.Soft())
 
14
  from speechbrain.inference.separation import SepformerSeparation
15
  from pydub import AudioSegment
16
 
17
+ # Disables TF32 for to prevent lower accuracy in pyannote
18
  torch.backends.cuda.matmul.allow_tf32 = False
19
  torch.backends.cudnn.allow_tf32 = False
20
 
21
+ # Temporary Directory Intializing
22
 
 
 
23
  temp_dir = tempfile.gettempdir()
24
+ print(f"temp directory created: {temp_dir}")
25
 
26
  @spaces.GPU
27
  def process_audio(mixture):
28
+ # GPU processing availability check
29
+ device = "cuda" if torch.cuda.is_available() else "cpu"
30
+
31
+ # Hugging Face authentication token grab
32
+ token = os.environ.get("HF_TOKEN")
33
 
34
  # Name of separated file folder
35
  sources = Path(temp_dir) / "sources"
 
38
  Path.mkdir(sources, exist_ok=True)
39
 
40
  # Downloads RE-SepFormer model from huggingface buckets to space local directory
41
+ # sync_bucket("hf://buckets/Jahmori-R/PureVoiceAISpace-storage", "./local")
42
+
43
+
44
 
45
  # ---------------------------------------------------------
46
+ # 1. INITIALIZE MODELS
47
  # ---------------------------------------------------------
48
 
49
  print("Intializing models...")
 
51
  # Loading Resepformer Model
52
  try:
53
  separator = SepformerSeparation.from_hparams(
54
+ source=f"Jahmori-R/resepformer-librimix2spk",
55
  hparams_file="hyperparams.yaml",
56
+ run_opts={"device": device},
57
  )
 
 
 
 
 
 
 
 
58
  print("RE-SepFormer model successfully loaded")
59
  except Exception as e:
60
  print(f"Failed to load RE-SepFormer model. Error: {e}")
61
  raise SystemExit()
62
 
63
+ # Loading Pyannote Model
64
+
65
  try:
66
  diarize = Pipeline.from_pretrained(
67
  "pyannote/speaker-diarization-community-1", token=token
68
  )
69
+ # Assigning GPU processing to model
70
  diarize.to(torch.device("cuda"))
71
  print("Pyannote model successfully loaded")
72
  except Exception as e:
 
74
  raise SystemExit()
75
 
76
  # Loading Whisper Model
77
+ transformers.logging.set_verbosity_error() # Prevents WhisperAI logging in cmd line
 
78
  try:
79
  transcription_model_id = "openai/whisper-large-v3-turbo"
80
  torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
 
105
  # LOAD & SEPARATE AUDIO
106
  # ---------------------------------------------------------
107
 
 
108
 
109
+ # Calling ReSepformer model onto audio source
110
  assert separator is not None, "Failed to load the Sepformer model."
111
 
112
+ # Running mixed audio onto separation model
113
  print("Separating Audio Mixture")
114
  est_sources = separator.separate_file(mixture)
115
  print("Mixture separated")
116
 
117
+ # Saving separated sources to directory
118
  torchaudio.save(
119
  f"{sources}/speaker_1.wav", est_sources[:, :, 0].detach().cpu(), 16000
120
  )
 
123
  )
124
 
125
  # ---------------------------------------------------------
126
+ # VAD + TRANSCRIPTION ON SOURCES
127
  # ---------------------------------------------------------
128
 
129
  # Dictionary to store speaker information
130
  unfiltered_log = [[], []]
 
131
 
132
  # Storing both speakers
133
  file_paths = sorted(
134
+ # Glob finds all similar files
135
  glob.glob(f"{sources}/speaker_*.wav")
136
+ )
137
 
138
  spk_1 = file_paths[0]
139
  spk_2 = file_paths[1]
 
146
  print(f"Diarizing {speaker_key}")
147
  diarization = diarize(path, num_speakers=1)
148
 
149
+ # Storing speaker voice activity timestamps to dictionary
150
  for turn, _ in diarization.speaker_diarization:
151
  unfiltered_log[i].append(
152
  {
 
156
  }
157
  )
158
 
159
+
160
+ # Creating audio segment from detected voice activity
161
  audio_seg = AudioSegment.from_file(path)
162
 
163
  print("Transcribing Chunks...")
 
168
  end_ms = segment["end"] * 1000
169
  chunk = audio_seg[start_ms:end_ms]
170
 
171
+ # Converting pydub AudioSegment to numpy array for whisper model
172
  samples = np.array(chunk.get_array_of_samples())
173
  samples = samples.astype(np.float32) / 32768.0
174
 
175
+ # Generating transcription given chunk segment
176
  transcription = transcribe(
177
  samples,
178
  generate_kwargs={
 
180
  "condition_on_prev_tokens": True,
181
  },
182
  )
183
+ # Appending each segment transcription to dictionary
184
  segment["transcription"] = transcription
185
 
186
  # Combined both speaker logs
 
189
  # Sorting by start time of timestamps
190
  filtered_log = sorted(combined_log, key=lambda x: x["start"])
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  print("Task Finished")
193
  return (spk_1, spk_2, filtered_log)
194
 
195
 
196
+ # Custom css for gradio interface
197
  css = """
198
 
199
  #centered-row {
 
202
  }
203
  """
204
 
205
+ # Gradio layout configuration
206
  with gr.Blocks(css=css) as demo:
207
  gr.Markdown("PureVoiceAI")
208
  with gr.Row(variant="panel"):
 
230
  )
231
 
232
  if __name__ == "__main__":
233
+ demo.launch(allowed_paths=[temp_dir], theme=gr.themes.Soft())
pretrained_models/resepformer-librimix2spk/decoder.ckpt DELETED
@@ -1 +0,0 @@
1
- /home/jahmori/.cache/huggingface/hub/models--Jahmori-R--resepformer-librimix2spk/snapshots/b1986fdc97f0dcfd2f738fb8083c7acb661ab783/decoder.ckpt
 
 
pretrained_models/resepformer-librimix2spk/encoder.ckpt DELETED
@@ -1 +0,0 @@
1
- /home/jahmori/.cache/huggingface/hub/models--Jahmori-R--resepformer-librimix2spk/snapshots/b1986fdc97f0dcfd2f738fb8083c7acb661ab783/encoder.ckpt
 
 
pretrained_models/resepformer-librimix2spk/hyperparams.yaml DELETED
@@ -1 +0,0 @@
1
- /home/jahmori/.cache/huggingface/hub/models--Jahmori-R--resepformer-librimix2spk/snapshots/b1986fdc97f0dcfd2f738fb8083c7acb661ab783/hyperparams.yaml
 
 
pretrained_models/resepformer-librimix2spk/masknet.ckpt DELETED
@@ -1 +0,0 @@
1
- /home/jahmori/.cache/huggingface/hub/models--Jahmori-R--resepformer-librimix2spk/snapshots/b1986fdc97f0dcfd2f738fb8083c7acb661ab783/masknet.ckpt
 
 
requirements.txt CHANGED
@@ -37,8 +37,8 @@ frozenlist==1.8.0
37
  fsspec==2026.7.0
38
  future==1.0.0
39
  googleapis-common-protos==1.75.3
40
- gradio==6.28.0
41
- gradio_client==2.7.1
42
  greenlet==3.5.6
43
  groovy==0.1.2
44
  grpcio==1.84.0
 
37
  fsspec==2026.7.0
38
  future==1.0.0
39
  googleapis-common-protos==1.75.3
40
+ gradio==6.26.0
41
+ gradio_client==2.6.1
42
  greenlet==3.5.6
43
  groovy==0.1.2
44
  grpcio==1.84.0