Add audio separation web app and verified recovery sources
This commit is contained in:
1 parent
b5ee7c1e01
commit
433e6709cf
25 files changed
+381
-46
No files matched your search
+10
-8
@@ -8,17 +8,19 @@ class SeparatorTests:
|
||||
self.root=Path(root);self.runtime=runtime;self.scheduler=scheduler;self.lock=threading.RLock();self.job=None;self.process=None;self.cancel=threading.Event();self.thread=None
|
||||
def status(self):
|
||||
with self.lock:return dict(job=dict(self.job) if self.job else None)
|
||||
def start(self,model,audio):
|
||||
def start(self,model,audio,target=None,full=False):
|
||||
targets={'vocals':'model_bs_roformer_ep_317_sdr_12.9755.ckpt','drums':'htdemucs_ft.yaml','bass':'htdemucs_ft.yaml','guitar':'htdemucs_6s.yaml','piano':'htdemucs_6s.yaml','other':'htdemucs_6s.yaml'}
|
||||
if target is not None and targets.get(target)!=model:raise ValueError('Zielspur und Modellpaket passen nicht zusammen.')
|
||||
if not isinstance(model,str) or model not in MODELS or not self.runtime.status()['installed'] or not self.runtime.model_ready(model):raise ValueError('Laufzeit und Modellpaket zuerst unter Audio Separator einrichten.')
|
||||
if not isinstance(audio,bytes) or not 44<=len(audio)<=8*1024**2:raise ValueError('WAV bis 8 MiB erforderlich.')
|
||||
if not isinstance(audio,bytes) or not 44<=len(audio)<=(256 if full else 8)*1024**2:raise ValueError('WAV überschreitet die erlaubte Upload-Größe.')
|
||||
try:
|
||||
with wave.open(io.BytesIO(audio)) as wav:
|
||||
if wav.getnchannels() not in (1,2) or wav.getsampwidth()!=2 or not 8000<=wav.getframerate()<=48000 or not 0<wav.getnframes()/wav.getframerate()<=30:raise ValueError('PCM-WAV, 16 Bit, Mono/Stereo, maximal 30 Sekunden erforderlich.')
|
||||
if wav.getnchannels() not in (1,2) or wav.getsampwidth()!=2 or not 8000<=wav.getframerate()<=48000 or not 0<wav.getnframes()/wav.getframerate()<=(1200 if full else 30):raise ValueError('PCM-WAV, 16 Bit, Mono/Stereo, maximal 30 Sekunden erforderlich.')
|
||||
if len(wav.readframes(wav.getnframes()))!=wav.getnframes()*wav.getnchannels()*2:raise ValueError('WAV-Datei ist unvollständig.')
|
||||
except (wave.Error,EOFError):raise ValueError('Ungültige WAV-Datei.') from None
|
||||
with self.lock:
|
||||
if self.job and self.job['state']=='running':raise ValueError('Trenntest läuft bereits.')
|
||||
self.cancel.clear();self.root.mkdir(parents=True,exist_ok=True,mode=0o700);self.job=dict(id=uuid.uuid4().hex,state='running',phase='Wartet auf GPU-Freigabe',started_at=time.time(),files=[])
|
||||
self.target=target;self.full=full;self.cancel.clear();self.root.mkdir(parents=True,exist_ok=True,mode=0o700);self.job=dict(id=uuid.uuid4().hex,state='running',phase='Wartet auf GPU-Freigabe',started_at=time.time(),files=[])
|
||||
self.thread=threading.Thread(target=self._run,args=(model,audio),daemon=True);self.thread.start();return dict(self.job)
|
||||
def _run(self,model,audio):
|
||||
release=None;directory=self.root/self.job['id'];state='failed';phase='Trennung fehlgeschlagen.'
|
||||
@@ -30,12 +32,12 @@ class SeparatorTests:
|
||||
with self.lock:
|
||||
if self.cancel.is_set():raise InterruptedError()
|
||||
self.job.update(phase='Modell laden und Audio trennen',gpu=gpu['name'])
|
||||
self.process=subprocess.Popen([str(self.runtime.paths()[0]),str(Path(__file__).with_name('separator_worker.py')),str(self.runtime.root/'models'),model,str(directory/'input.wav'),str(directory)],stdout=subprocess.DEVNULL,stderr=diagnostics,start_new_session=True,env=dict(os.environ,CUDA_VISIBLE_DEVICES=gpu['uuid'],OMP_NUM_THREADS='6'))
|
||||
self.process=subprocess.Popen([str(self.runtime.paths()[0]),str(Path(__file__).with_name('separator_worker.py')),str(self.runtime.root/'models'),model,str(directory/'input.wav'),str(directory),getattr(self,'target',None) or ''],stdout=subprocess.DEVNULL,stderr=diagnostics,start_new_session=True,env=dict(os.environ,CUDA_VISIBLE_DEVICES=gpu['uuid'],OMP_NUM_THREADS='6'))
|
||||
# Drain diagnostics into a bounded temporary file rather than retaining user data.
|
||||
deadline=time.monotonic()+600
|
||||
deadline=time.monotonic()+(7200 if getattr(self,'full',False) else 600)
|
||||
while self.process.poll() is None:
|
||||
if self.cancel.wait(.2):raise InterruptedError()
|
||||
if time.monotonic()>deadline:raise ValueError('Trenntest überschreitet zehn Minuten.')
|
||||
if time.monotonic()>deadline:raise ValueError('Trennauftrag überschreitet das Zeitlimit.')
|
||||
with self.lock:self.job['elapsed_seconds']=round(time.time()-self.job['started_at'])
|
||||
diagnostics.close();error=(directory/'worker.tmp').read_bytes()[-65536:].decode(errors='replace')
|
||||
if self.process.returncode:raise ValueError('GPU-Speicher reicht nicht (OOM).' if 'out of memory' in error.lower() else 'Audio Separator hat den Test abgebrochen. Modellpaket und Laufzeit prüfen.')
|
||||
@@ -67,5 +69,5 @@ class SeparatorTests:
|
||||
if type(index) is not int or not 0<=index<len(names) or Path(names[index]).name!=names[index]:raise ValueError('Ungültige Spur.')
|
||||
path=directory/names[index]
|
||||
if path.is_symlink() or path.resolve().parent!=directory.resolve():raise ValueError('Ungültiger Ergebnispfad.')
|
||||
if path.stat().st_size>32*1024**2:raise ValueError('Spur zu groß.')
|
||||
if path.stat().st_size>512*1024**2:raise ValueError('Spur zu groß.')
|
||||
return path.read_bytes()
|
||||
Reference in new issue
Block a user