Major updates
- UI is now on customtkinter - new environment.yml file - entry point is now in main.py - minor improvements in audio_transcription
This commit is contained in:
+12
-191
@@ -1,195 +1,16 @@
|
||||
name: notecast
|
||||
channels:
|
||||
- conda-forge
|
||||
- defaults
|
||||
- nvidia
|
||||
- pytorch
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- accelerate=1.10.1=pyhcf101f3_0
|
||||
- aiohappyeyeballs=2.6.1=pyhd8ed1ab_0
|
||||
- aiohttp=3.12.15=pyh7db6752_0
|
||||
- aiosignal=1.4.0=pyhd8ed1ab_0
|
||||
- aom=3.6.0=hd77b12b_0
|
||||
- arrow-cpp=19.0.0=h82e8c66_4
|
||||
- async-timeout=5.0.1=pyhd8ed1ab_1
|
||||
- attrs=25.3.0=pyh71513ae_0
|
||||
- aws-c-auth=0.9.0=hd490b63_15
|
||||
- aws-c-cal=0.9.2=hd8a8e38_0
|
||||
- aws-c-common=0.12.3=h2466b09_0
|
||||
- aws-c-compression=0.3.1=h5d0e663_5
|
||||
- aws-c-event-stream=0.5.5=ha416645_0
|
||||
- aws-c-http=0.10.2=h81282ae_2
|
||||
- aws-c-io=0.20.1=hf7624bd_1
|
||||
- aws-c-mqtt=0.13.1=h5c1ae27_3
|
||||
- aws-c-s3=0.8.3=h1e843c7_0
|
||||
- aws-c-sdkutils=0.2.4=h5d0e663_0
|
||||
- aws-checksums=0.2.7=h5d0e663_1
|
||||
- aws-crt-cpp=0.32.10=h8abd1a4_2
|
||||
- aws-sdk-cpp=1.11.528=hd293286_1
|
||||
- blas=1.0=mkl
|
||||
- bottleneck=1.4.2=py312h4b0e54e_0
|
||||
- brotlicffi=1.0.9.2=py312h5da7b33_1
|
||||
- bzip2=1.0.8=h2bbff1b_6
|
||||
- c-ares=1.34.5=h2466b09_0
|
||||
- ca-certificates=2025.8.3=h4c7d964_0
|
||||
- cairo=1.18.4=he9e932c_0
|
||||
- certifi=2025.8.3=py312haa95532_0
|
||||
- cffi=1.17.1=py312h827c3e9_1
|
||||
- charset-normalizer=3.3.2=pyhd3eb1b0_0
|
||||
- colorama=0.4.6=pyhd8ed1ab_1
|
||||
- cuda-cccl=13.0.50=h23517cc_1
|
||||
- cuda-cccl_win-64=13.0.50=hc667259_1
|
||||
- cuda-cudart=12.4.127=0
|
||||
- cuda-cudart-dev=12.4.127=0
|
||||
- cuda-cupti=12.4.127=0
|
||||
- cuda-libraries=12.4.1=0
|
||||
- cuda-libraries-dev=12.4.1=0
|
||||
- cuda-nvrtc=12.4.127=0
|
||||
- cuda-nvrtc-dev=12.4.127=0
|
||||
- cuda-nvtx=12.4.127=0
|
||||
- cuda-opencl=13.0.39=0
|
||||
- cuda-opencl-dev=13.0.39=0
|
||||
- cuda-profiler-api=13.0.39=0
|
||||
- cuda-runtime=12.4.1=0
|
||||
- cuda-version=13.0=3
|
||||
- datasets=4.0.0=pyhcf101f3_0
|
||||
- dav1d=1.2.1=h2bbff1b_0
|
||||
- dill=0.3.8=pyhd8ed1ab_0
|
||||
- expat=2.7.1=h8ddb27b_0
|
||||
- ffmpeg=4.3.1=ha925a31_0
|
||||
- filelock=3.17.0=py312haa95532_0
|
||||
- fontconfig=2.14.1=hb33846d_3
|
||||
- freeglut=3.4.0=h8a1e904_1
|
||||
- freetype=2.13.3=h0620614_0
|
||||
- fribidi=1.0.10=h62dcd97_0
|
||||
- frozenlist=1.7.0=pyhf298e5d_0
|
||||
- fsspec=2025.3.0=pyhd8ed1ab_0
|
||||
- gflags=2.2.2=he0c23c2_1005
|
||||
- giflib=5.2.2=h7edc060_0
|
||||
- glog=0.5.0=h4797de2_0
|
||||
- gmp=6.3.0=h537511b_0
|
||||
- gmpy2=2.2.1=py312h827c3e9_0
|
||||
- graphite2=1.3.14=hd77b12b_1
|
||||
- harfbuzz=10.2.0=he2f9f60_1
|
||||
- hf-xet=1.1.8=py312h79d111c_0
|
||||
- huggingface_hub=0.34.4=pyhd8ed1ab_1
|
||||
- icu=73.1=h6c2663c_0
|
||||
- idna=3.7=py312haa95532_0
|
||||
- intel-openmp=2023.1.0=h59b6b97_46320
|
||||
- jinja2=3.1.6=py312haa95532_0
|
||||
- jpeg=9e=h827c3e9_3
|
||||
- khronos-opencl-icd-loader=2024.05.08=h8cc25b3_0
|
||||
- lcms2=2.16=h62be587_1
|
||||
- lerc=4.0.0=h5da7b33_0
|
||||
- libabseil=20250127.0=cxx17_h4eb7d71_0
|
||||
- libavif=1.1.1=h827c3e9_0
|
||||
- libbrotlicommon=1.0.9=hcfcfb64_9
|
||||
- libbrotlidec=1.0.9=hcfcfb64_9
|
||||
- libbrotlienc=1.0.9=hcfcfb64_9
|
||||
- libcublas=12.4.5.8=0
|
||||
- libcublas-dev=12.4.5.8=0
|
||||
- libcufft=11.2.1.3=0
|
||||
- libcufft-dev=11.2.1.3=0
|
||||
- libcurand=10.4.0.35=0
|
||||
- libcurand-dev=10.4.0.35=0
|
||||
- libcurl=8.15.0=h2300eb9_0
|
||||
- libcusolver=11.6.1.9=0
|
||||
- libcusolver-dev=11.6.1.9=0
|
||||
- libcusparse=12.3.1.170=0
|
||||
- libcusparse-dev=12.3.1.170=0
|
||||
- libdeflate=1.22=h5bf469e_0
|
||||
- libffi=3.4.4=hd77b12b_1
|
||||
- libglib=2.84.2=h405b238_0
|
||||
- libgrpc=1.71.0=hf4237ab_0
|
||||
- libiconv=1.16=h2bbff1b_3
|
||||
- libjpeg-turbo=2.0.0=h196d8e1_0
|
||||
- libnpp=12.2.5.30=0
|
||||
- libnpp-dev=12.2.5.30=0
|
||||
- libnvfatbin=13.0.39=0
|
||||
- libnvfatbin-dev=13.0.39=0
|
||||
- libnvjitlink=12.4.127=0
|
||||
- libnvjitlink-dev=12.4.127=0
|
||||
- libnvjpeg=12.3.1.117=0
|
||||
- libnvjpeg-dev=12.3.1.117=0
|
||||
- libpng=1.6.39=h8cc25b3_0
|
||||
- libprotobuf=5.29.3=h65a231f_1
|
||||
- libre2-11=2024.07.02=hd248061_3
|
||||
- libssh2=1.11.1=h2addb87_0
|
||||
- libthrift=0.15.0=ha2884a9_3
|
||||
- libtiff=4.7.0=h404307b_0
|
||||
- libuv=1.48.0=h827c3e9_0
|
||||
- libwebp=1.3.2=h18467be_1
|
||||
- libwebp-base=1.3.2=h3d04722_1
|
||||
- libxml2=2.13.8=h866ff63_0
|
||||
- lz4-c=1.9.4=h2bbff1b_1
|
||||
- markupsafe=3.0.2=py312h827c3e9_0
|
||||
- mkl=2023.1.0=h6b88ed4_46358
|
||||
- mkl-service=2.4.0=py312h827c3e9_2
|
||||
- mkl_fft=1.3.11=py312h827c3e9_0
|
||||
- mkl_random=1.2.8=py312h0158946_0
|
||||
- mpc=1.3.1=h827c3e9_0
|
||||
- mpfr=4.2.1=h56c3642_0
|
||||
- mpmath=1.3.0=py312haa95532_0
|
||||
- multidict=6.6.3=pyh62beb40_0
|
||||
- multiprocess=0.70.15=py312haa95532_0
|
||||
- networkx=3.5=py312haa95532_0
|
||||
- numexpr=2.11.0=py312hdb065b2_0
|
||||
- numpy=2.3.1=py312h5f75535_0
|
||||
- numpy-base=2.3.1=py312h23d94f8_0
|
||||
- openjpeg=2.5.2=h9b5d1b5_1
|
||||
- openssl=3.5.2=h725018a_0
|
||||
- orc=2.1.1=hd1c1d5c_0
|
||||
- packaging=25.0=pyh29332c3_1
|
||||
- pandas=2.3.2=py312ha5e6156_0
|
||||
- pcre2=10.42=h0ff8eda_1
|
||||
- pillow=11.3.0=py312hb328d1f_0
|
||||
- pip=25.2=pyhc872135_0
|
||||
- pixman=0.46.4=h4043f72_0
|
||||
- propcache=0.3.1=pyhe1237c8_0
|
||||
- psutil=5.9.0=py312h827c3e9_1
|
||||
- pyarrow=19.0.0=py312h5da7b33_1
|
||||
- pycparser=2.21=pyhd3eb1b0_0
|
||||
- pysocks=1.7.1=py312haa95532_0
|
||||
- python=3.12.11=h716150d_0
|
||||
- python-dateutil=2.9.0.post0=pyhe01879c_2
|
||||
- python-tzdata=2025.2=pyhd8ed1ab_0
|
||||
- python-xxhash=3.5.0=py312h827c3e9_0
|
||||
- pytorch=2.5.1=py3.12_cuda12.4_cudnn9_0
|
||||
- pytorch-cuda=12.4=h3fd98bf_7
|
||||
- pytorch-mutex=1.0=cuda
|
||||
- pytz=2025.2=pyhd8ed1ab_0
|
||||
- pyyaml=6.0.2=py312h827c3e9_0
|
||||
- re2=2024.07.02=haf4117d_3
|
||||
- regex=2024.11.6=py312h827c3e9_0
|
||||
- requests=2.32.5=py312haa95532_0
|
||||
- safetensors=0.5.3=py312h44068b5_0
|
||||
- setuptools=72.1.0=py312haa95532_0
|
||||
- six=1.17.0=pyhe01879c_1
|
||||
- snappy=1.2.2=h7fa0ca8_0
|
||||
- sqlite=3.50.2=hda9a48d_1
|
||||
- sympy=1.14.0=py312haa95532_0
|
||||
- tbb=2021.8.0=h59b6b97_0
|
||||
- tk=8.6.15=hf199647_0
|
||||
- tokenizers=0.21.0=py312h482ea96_0
|
||||
- tqdm=4.67.1=pyhd8ed1ab_1
|
||||
- transformers=4.55.4=pyhd8ed1ab_0
|
||||
- typing-extensions=4.15.0=py312haa95532_0
|
||||
- typing_extensions=4.15.0=py312haa95532_0
|
||||
- tzdata=2025b=h04d1e81_0
|
||||
- ucrt=10.0.22621.0=haa95532_0
|
||||
- urllib3=2.5.0=py312haa95532_0
|
||||
- utf8proc=2.6.1=h2bbff1b_1
|
||||
- vc=14.42=haa95532_5
|
||||
- vc14_runtime=14.44.35208=h4927774_10
|
||||
- vs2015_runtime=14.44.35208=ha6b5a95_10
|
||||
- wheel=0.45.1=py312haa95532_0
|
||||
- win_inet_pton=1.1.0=py312haa95532_0
|
||||
- xxhash=0.8.0=h8ffe710_3
|
||||
- xz=5.6.4=h4754444_1
|
||||
- yaml=0.2.5=he774522_0
|
||||
- yarl=1.20.1=pyhe1237c8_0
|
||||
- zlib=1.2.13=h8cc25b3_1
|
||||
- zstd=1.5.6=h8880b57_0
|
||||
- pip:
|
||||
- torchaudio==2.5.1
|
||||
- torchvision==0.20.1
|
||||
- torchvision
|
||||
- pytorch
|
||||
- torchaudio
|
||||
- transformers
|
||||
- accelerate
|
||||
- ffmpeg
|
||||
- pyinstaller
|
||||
- customtkinter
|
||||
- isort
|
||||
- mypy
|
||||
- black
|
||||
@@ -1,13 +1,5 @@
|
||||
from transcription.audio_transcription import AudioTranscription
|
||||
from transcription.torch_checker import check_torch
|
||||
from utils.logger import setup_logger
|
||||
from ui.ui import TranscriberApp
|
||||
|
||||
logger = setup_logger("main")
|
||||
|
||||
check_torch()
|
||||
|
||||
try:
|
||||
track = AudioTranscription("sample.mp3")
|
||||
print(track.transcribe_audio())
|
||||
except Exception as e:
|
||||
logger.error(f"Execution error: {e}")
|
||||
if __name__ == "__main__":
|
||||
app = TranscriberApp()
|
||||
app.mainloop()
|
||||
@@ -8,7 +8,7 @@ import time
|
||||
import math
|
||||
from tqdm import tqdm
|
||||
|
||||
# TODO: rename naming
|
||||
# TODO: implement transcription with shift
|
||||
class AudioTranscription:
|
||||
model_name = "openai/whisper-large-v2"
|
||||
|
||||
@@ -53,6 +53,7 @@ class AudioTranscription:
|
||||
|
||||
self.chunks: list = []
|
||||
self.batches: list = []
|
||||
self.all_transcription: list = []
|
||||
try:
|
||||
logger.info("Loading model WhisperProcessor...")
|
||||
self.processor = WhisperProcessor.from_pretrained(self.model_name)
|
||||
@@ -71,15 +72,15 @@ class AudioTranscription:
|
||||
logger.error(f"Unable to load file {self.filepath}: {e}")
|
||||
raise
|
||||
|
||||
def resample(self) -> None:
|
||||
def _resample(self) -> None:
|
||||
self.waveform = torchaudio.functional.resample(self.waveform, self.sampling_rate, 16000)
|
||||
|
||||
def to_mono(self):
|
||||
def _to_mono(self):
|
||||
if self.waveform.shape[0] > 1:
|
||||
self.waveform = self.waveform.mean(dim=0, keepdim=True)
|
||||
self.waveform = self.waveform.squeeze(0)
|
||||
|
||||
def split_to_chunks(self, chunk_length_s: int = 30) -> None:
|
||||
def _split_to_chunks(self, chunk_length_s: int = 30) -> None:
|
||||
self.logger.info(f"Splitting audio on chunks...")
|
||||
|
||||
self.chunk_size = chunk_length_s * 16000 # 16kHz after resampling
|
||||
@@ -95,15 +96,14 @@ class AudioTranscription:
|
||||
chunk = self.waveform[start:end].cpu().numpy().astype("float32")
|
||||
self.chunks.append(chunk)
|
||||
|
||||
def resplit_to_batches(self) -> None:
|
||||
def _resplit_to_batches(self) -> None:
|
||||
self.logger.info(f"Splitting chunks into batches...")
|
||||
self.batches = []
|
||||
for i in range(0, len(self.chunks), self.custom_batch_length):
|
||||
batch = self.chunks[i:i + self.custom_batch_length]
|
||||
self.batches.append(batch)
|
||||
|
||||
|
||||
def process_all_batches(self) -> None:
|
||||
def _process_all_batches(self) -> None:
|
||||
start_time = time.time()
|
||||
try:
|
||||
self.all_transcription = []
|
||||
@@ -136,9 +136,10 @@ class AudioTranscription:
|
||||
|
||||
|
||||
def transcribe_audio(self) -> str:
|
||||
self.resample()
|
||||
self.to_mono()
|
||||
self.split_to_chunks()
|
||||
self.resplit_to_batches()
|
||||
self.process_all_batches()
|
||||
# TODO: maybe something else, not str?
|
||||
self._resample()
|
||||
self._to_mono()
|
||||
self._split_to_chunks()
|
||||
self._resplit_to_batches()
|
||||
self._process_all_batches()
|
||||
return " ".join(self.all_transcription)
|
||||
@@ -0,0 +1,9 @@
|
||||
from transcription.device_configuration import DeviceConfiguration
|
||||
|
||||
# TODO: implement saving & removing configuration
|
||||
def save_configuration(cfg: DeviceConfiguration):
|
||||
config = {
|
||||
"Model": cfg.model_name,
|
||||
"Batch Size": cfg.batch_size,
|
||||
"Data Type": cfg.data_type
|
||||
}
|
||||
@@ -1,123 +1,245 @@
|
||||
import os
|
||||
import threading
|
||||
import queue
|
||||
import tkinter as tk
|
||||
from tkinter.scrolledtext import ScrolledText
|
||||
from ui.ui_log_handler import UILogHandler, setup_ui_logger
|
||||
from tkinter import scrolledtext, filedialog, messagebox
|
||||
|
||||
import customtkinter as ctk
|
||||
import torch
|
||||
|
||||
from ui.ui_log_handler import setup_ui_logger
|
||||
from transcription.torch_checker import check_torch
|
||||
from transcription.device_configuration import DeviceConfiguration
|
||||
from transcription.audio_transcription import AudioTranscription
|
||||
|
||||
def main():
|
||||
root = tk.Tk()
|
||||
root.title("Audio Transcriptor")
|
||||
root.geometry("800x600")
|
||||
|
||||
for col in range(4):
|
||||
root.grid_columnconfigure(col, weight=1)
|
||||
root.grid_rowconfigure(6, weight=1)
|
||||
WINDOW_WIDTH = 900
|
||||
WINDOW_HEIGHT = 650
|
||||
|
||||
### Buttons selector
|
||||
check_torch_baton = tk.Button(root, text="Check Torch")
|
||||
check_torch_baton.grid(row=0, column=0, padx=5, pady=5, sticky="ew")
|
||||
|
||||
# TODO: implement saving function
|
||||
save_configuration_baton = tk.Button(root, text="Save configuration")
|
||||
save_configuration_baton.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
|
||||
class TranscriberApp(ctk.CTk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("Notecast")
|
||||
self.geometry(f"{WINDOW_WIDTH}x{WINDOW_HEIGHT}")
|
||||
ctk.set_appearance_mode("System")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
# TODO: implement deleting function
|
||||
delete_configuration_baton = tk.Button(root, text="Delete configuration")
|
||||
delete_configuration_baton.grid(row=0, column=2, padx=5, pady=5, sticky="ew")
|
||||
# states
|
||||
self.progress_queue = queue.Queue()
|
||||
self.transcribe_thread = None
|
||||
self.stop_flag = threading.Event()
|
||||
|
||||
start_transcription_baton = tk.Button(root, text="Transcript")
|
||||
start_transcription_baton.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
|
||||
# user variables
|
||||
self.model_var = tk.StringVar(value="openai/whisper-large-v2")
|
||||
self.batch_var = tk.StringVar(value="32")
|
||||
self.dtype_var = tk.StringVar(value="torch.float16")
|
||||
self.chunk_var = tk.StringVar(value="30")
|
||||
|
||||
### Model options selector
|
||||
model_options = [
|
||||
"openai/whisper-large-v2",
|
||||
"openai/whisper-large",
|
||||
"openai/whisper-medium",
|
||||
"openai/whisper-small",
|
||||
"openai/whisper-tiny"
|
||||
]
|
||||
selected_model = tk.StringVar(value=model_options[0])
|
||||
label_model = tk.Label(root, text="Model name:")
|
||||
label_model.grid(row=1, column=0, sticky="w", pady=5, padx=5)
|
||||
dropdown_model_selection = tk.OptionMenu(root, selected_model, *model_options)
|
||||
dropdown_model_selection.grid(row=1, column=1, sticky="ew", pady=5, padx=5)
|
||||
# settings device options
|
||||
device_opts = []
|
||||
if torch.cuda.is_available():
|
||||
device_opts.append("cuda")
|
||||
if torch.backends.mps.is_available():
|
||||
device_opts.append("mps")
|
||||
device_opts.append("cpu")
|
||||
self.device_var = tk.StringVar(value=device_opts[0])
|
||||
self.device_opts = device_opts
|
||||
|
||||
### Batch size selector
|
||||
batch_sizes = ["32", "16", "8", "4", "2"]
|
||||
selected_batch_size = tk.StringVar(value=batch_sizes[0])
|
||||
label_batch_size = tk.Label(root, text="Batch size:")
|
||||
label_batch_size.grid(row=1, column=2, sticky="w", pady=5, padx=5)
|
||||
dropdown_batch_size_selection = tk.OptionMenu(root, selected_batch_size, *batch_sizes)
|
||||
dropdown_batch_size_selection.grid(row=1, column=3, sticky="ew", pady=5, padx=5)
|
||||
self.input_file_var = tk.StringVar()
|
||||
self.output_file_var = tk.StringVar()
|
||||
|
||||
### Data type selector
|
||||
data_types = ["torch.float16", "torch.float32", "torch.bfloat16"]
|
||||
selected_data_type = tk.StringVar(value=data_types[0])
|
||||
label_data_type = tk.Label(root, text="Data type:")
|
||||
label_data_type.grid(row=2, column=0, sticky="w", pady=5, padx=5)
|
||||
dropdown_data_type_selection = tk.OptionMenu(root, selected_data_type, *data_types)
|
||||
dropdown_data_type_selection.grid(row=2, column=1, sticky="ew", pady=5, padx=5)
|
||||
# tabs packing
|
||||
self.tabview = ctk.CTkTabview(self)
|
||||
self.tabview.pack(expand=True, fill="both", padx=10, pady=10)
|
||||
|
||||
### Chunk length selector
|
||||
chunk_lengths = ["30", "25", "20", "15", "10", "5"]
|
||||
selected_chunk_length = tk.StringVar(value=chunk_lengths[0])
|
||||
label_chunk_length = tk.Label(root, text="Chunk length:")
|
||||
label_chunk_length.grid(row=2, column=2, sticky="w", pady=5, padx=5)
|
||||
dropdown_chunk_length_selection = tk.OptionMenu(root, selected_chunk_length, *chunk_lengths)
|
||||
dropdown_chunk_length_selection.grid(row=2, column=3, sticky="ew", pady=5, padx=5)
|
||||
self.transcript_tab = self.tabview.add("Transcription")
|
||||
self.settings_tab = self.tabview.add("Settings")
|
||||
|
||||
# TODO: add device selector (cuda/mps/cpu)
|
||||
self._build_transcription_tab()
|
||||
self._build_settings_tab()
|
||||
|
||||
### Filepath (input)
|
||||
# TODO: add checker if path is valid/invalid (i think in utils or something)
|
||||
label_file_path = tk.Label(root, text="Input filepath:")
|
||||
label_file_path.grid(row=3, column=0, sticky="w", pady=5, padx=5)
|
||||
file_path = tk.Text(root, height=1)
|
||||
file_path.grid(row=3, column=1, columnspan=3, sticky="ew", pady=5, padx=5)
|
||||
# logger
|
||||
self.ui_logger = setup_ui_logger(self.log_box)
|
||||
|
||||
### Filepath (output)
|
||||
# TODO: add question mark here with tip while mouse is on it
|
||||
label_output_file_path = tk.Label(root, text="Output filepath:")
|
||||
label_output_file_path.grid(row=4, column=0, sticky="w", pady=5, padx=5)
|
||||
output_file_path = tk.Text(root, height=1)
|
||||
output_file_path.grid(row=4, column=1, columnspan=3, sticky="ew", pady=5, padx=5)
|
||||
# main transcription tab
|
||||
def _build_transcription_tab(self):
|
||||
# file selectors
|
||||
file_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
|
||||
file_frame.pack(padx=20, pady=10, fill="x")
|
||||
|
||||
def show_selections():
|
||||
ui_logger.info(f"Selected model: {selected_model.get()}")
|
||||
ui_logger.info(f"Selected batch size: {selected_batch_size.get()} chunks")
|
||||
ui_logger.info(f"Selected data type: {selected_data_type.get()}")
|
||||
|
||||
show_selections_baton = tk.Button(root, text="Show Selections", command=show_selections)
|
||||
show_selections_baton.grid(row=5, column=0, columnspan=4, pady=5, sticky="ew")
|
||||
|
||||
log_box = ScrolledText(root, wrap="word")
|
||||
log_box.grid(row=6, column=0, columnspan=4, sticky="nsew", padx=10, pady=5)
|
||||
ui_logger = setup_ui_logger(log_box)
|
||||
|
||||
def transcribe():
|
||||
current_device_config = DeviceConfiguration(
|
||||
device="cuda",
|
||||
model_name=selected_model.get(),
|
||||
batch_size=int(selected_batch_size.get()),
|
||||
chunk_length_s=30,
|
||||
data_type=selected_data_type.get()
|
||||
ctk.CTkLabel(file_frame, text="Input file:").pack(side="left", padx=5, pady=5)
|
||||
ctk.CTkEntry(file_frame, textvariable=self.input_file_var, width=400).pack(
|
||||
side="left", padx=5, pady=5, expand=True, fill="x"
|
||||
)
|
||||
Audio = AudioTranscription(
|
||||
filepath=file_path.get("1.0", "end-1c"),
|
||||
device_configuration=current_device_config,
|
||||
logger=ui_logger
|
||||
ctk.CTkButton(file_frame, text="Browse", command=self._browse_input).pack(
|
||||
side="left", padx=5, pady=5
|
||||
)
|
||||
transcription = Audio.transcribe_audio()
|
||||
with open(f"{file_path.get('1.0', 'end-1c')}.txt", "w") as output_file:
|
||||
output_file.write(transcription)
|
||||
|
||||
check_torch_baton.config(command=lambda: check_torch(ui_logger))
|
||||
start_transcription_baton.config(command=transcribe)
|
||||
out_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
|
||||
out_frame.pack(padx=20, pady=5, fill="x")
|
||||
ctk.CTkLabel(out_frame, text="Output file:").pack(side="left", padx=5, pady=5)
|
||||
ctk.CTkEntry(out_frame, textvariable=self.output_file_var, width=400).pack(
|
||||
side="left", padx=5, pady=5, expand=True, fill="x"
|
||||
)
|
||||
ctk.CTkButton(out_frame, text="Browse", command=self._browse_output).pack(
|
||||
side="left", padx=5, pady=5
|
||||
)
|
||||
|
||||
root.mainloop()
|
||||
# controls
|
||||
ctrl_frame = ctk.CTkFrame(self.transcript_tab, corner_radius=10)
|
||||
ctrl_frame.pack(padx=20, pady=10, fill="x")
|
||||
|
||||
ctk.CTkButton(ctrl_frame, text="Check Torch", command=self._check_torch).pack(
|
||||
side="left", padx=10, pady=5
|
||||
)
|
||||
self.start_btn = ctk.CTkButton(
|
||||
ctrl_frame, text="Start transcription", command=self._start_transcription
|
||||
)
|
||||
self.start_btn.pack(side="left", padx=10, pady=5)
|
||||
|
||||
self.stop_btn = ctk.CTkButton(
|
||||
ctrl_frame, text="Stop", command=self._stop_transcription, state="disabled"
|
||||
)
|
||||
self.stop_btn.pack(side="left", padx=10, pady=5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
# log box
|
||||
self.log_box = scrolledtext.ScrolledText(
|
||||
self.transcript_tab,
|
||||
wrap="word",
|
||||
height=20,
|
||||
font=("Consolas", 16),
|
||||
)
|
||||
self.log_box.pack(padx=20, pady=10, expand=True, fill="both")
|
||||
|
||||
def _build_settings_tab(self):
|
||||
pad = 20
|
||||
|
||||
### Model
|
||||
ctk.CTkLabel(self.settings_tab, text="Model:").pack(
|
||||
anchor="w", padx=pad, pady=(pad, 5)
|
||||
)
|
||||
ctk.CTkOptionMenu(
|
||||
self.settings_tab,
|
||||
variable=self.model_var,
|
||||
values=[
|
||||
"openai/whisper-large-v2",
|
||||
"openai/whisper-large",
|
||||
"openai/whisper-medium",
|
||||
"openai/whisper-small",
|
||||
"openai/whisper-tiny",
|
||||
],
|
||||
).pack(fill="x", padx=pad, pady=5)
|
||||
|
||||
### Batch size
|
||||
ctk.CTkLabel(self.settings_tab, text="Batch size:").pack(
|
||||
anchor="w", padx=pad, pady=(pad, 5)
|
||||
)
|
||||
ctk.CTkOptionMenu(
|
||||
self.settings_tab,
|
||||
variable=self.batch_var,
|
||||
values=["32", "16", "8", "4", "2"],
|
||||
).pack(fill="x", padx=pad, pady=5)
|
||||
|
||||
### Data type
|
||||
ctk.CTkLabel(self.settings_tab, text="Data type:").pack(
|
||||
anchor="w", padx=pad, pady=(pad, 5)
|
||||
)
|
||||
ctk.CTkOptionMenu(
|
||||
self.settings_tab,
|
||||
variable=self.dtype_var,
|
||||
values=["torch.float16", "torch.float32", "torch.bfloat16"],
|
||||
).pack(fill="x", padx=pad, pady=5)
|
||||
|
||||
### Chunk length
|
||||
ctk.CTkLabel(self.settings_tab, text="Chunk length (s):").pack(
|
||||
anchor="w", padx=pad, pady=(pad, 5)
|
||||
)
|
||||
ctk.CTkOptionMenu(
|
||||
self.settings_tab,
|
||||
variable=self.chunk_var,
|
||||
values=["30", "25", "20", "15", "10", "5"],
|
||||
).pack(fill="x", padx=pad, pady=5)
|
||||
|
||||
### Device
|
||||
ctk.CTkLabel(self.settings_tab, text="Device:").pack(
|
||||
anchor="w", padx=pad, pady=(pad, 5)
|
||||
)
|
||||
ctk.CTkOptionMenu(
|
||||
self.settings_tab,
|
||||
variable=self.device_var,
|
||||
values=self.device_opts,
|
||||
).pack(fill="x", padx=pad, pady=5)
|
||||
|
||||
# action buttons
|
||||
def _browse_input(self):
|
||||
path = filedialog.askopenfilename(
|
||||
title="Select input audio file",
|
||||
filetypes=[("Audio files", "*.wav *.mp3 *.m4a *.flac"), ("All files", "*.*")]
|
||||
)
|
||||
if path:
|
||||
self.input_file_var.set(path)
|
||||
|
||||
def _browse_output(self):
|
||||
path = filedialog.asksaveasfilename(
|
||||
title="Select output file",
|
||||
defaultextension=".txt",
|
||||
filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
|
||||
)
|
||||
if path:
|
||||
self.output_file_var.set(path)
|
||||
|
||||
def _check_torch(self):
|
||||
check_torch(self.ui_logger)
|
||||
|
||||
def _start_transcription(self):
|
||||
infile = self.input_file_var.get().strip()
|
||||
if not infile or not os.path.isfile(infile):
|
||||
messagebox.showerror("Error", "Please select a valid input file.")
|
||||
return
|
||||
|
||||
self.start_btn.configure(state="disabled")
|
||||
self.stop_btn.configure(state="normal")
|
||||
self.ui_logger.info("Starting transcription...")
|
||||
|
||||
self.stop_flag.clear()
|
||||
self.transcribe_thread = threading.Thread(
|
||||
target=self._transcribe_worker, args=(infile,), daemon=True
|
||||
)
|
||||
self.transcribe_thread.start()
|
||||
|
||||
def _stop_transcription(self):
|
||||
self.stop_flag.set()
|
||||
self.ui_logger.info("Stopping transcription...")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
|
||||
def _transcribe_worker(self, infile: str):
|
||||
try:
|
||||
config = DeviceConfiguration(
|
||||
device=self.device_var.get(),
|
||||
model_name=self.model_var.get(),
|
||||
batch_size=int(self.batch_var.get()),
|
||||
chunk_length_s=int(self.chunk_var.get()),
|
||||
data_type=self.dtype_var.get(),
|
||||
)
|
||||
Audio = AudioTranscription(
|
||||
filepath=infile,
|
||||
device_configuration=config,
|
||||
logger=self.ui_logger,
|
||||
)
|
||||
transcription = Audio.transcribe_audio()
|
||||
|
||||
outfile = self.output_file_var.get().strip()
|
||||
if not outfile:
|
||||
outfile = infile + ".txt"
|
||||
|
||||
with open(outfile, "w", encoding="utf-8") as f:
|
||||
f.write(transcription)
|
||||
|
||||
self.ui_logger.info(f"Transcription saved to {outfile}")
|
||||
except Exception as e:
|
||||
self.ui_logger.error(f"Error: {e}")
|
||||
messagebox.showerror("Error", str(e))
|
||||
finally:
|
||||
self.start_btn.configure(state="normal")
|
||||
self.stop_btn.configure(state="disabled")
|
||||
Reference in New Issue
Block a user