Text-to-Speech
Safetensors
MLX
English
mlx-audio
chatterbox_turbo
chatterbox-turbo
quantized
voice-cloning
6-bit
Instructions to use groxaxo/Chatterbox-Turbo-MLX-Community-6bit with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use groxaxo/Chatterbox-Turbo-MLX-Community-6bit with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download groxaxo/Chatterbox-Turbo-MLX-Community-6bit --local-dir Chatterbox-Turbo-MLX-Community-6bit
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
Download evaluation/scripts/decoder_fidelity.py from groxaxo/Chatterbox-Turbo-MLX-Community-6bit: direct link, hf CLI and curl.
- Browser
- Download file 4.02 kB
-
https://huggingface.co/groxaxo/Chatterbox-Turbo-MLX-Community-6bit/resolve/main/evaluation/scripts/decoder_fidelity.py
- Command line
-
hf download hf://groxaxo/Chatterbox-Turbo-MLX-Community-6bit/evaluation/scripts/decoder_fidelity.py
-
curl -L -o decoder_fidelity.py https://huggingface.co/groxaxo/Chatterbox-Turbo-MLX-Community-6bit/resolve/main/evaluation/scripts/decoder_fidelity.py
4.02 kB
| import os, sys, json, time, hashlib | |
| from pathlib import Path | |
| os.environ['HF_HUB_OFFLINE']='1' | |
| os.environ['TRANSFORMERS_OFFLINE']='1' | |
| os.environ['TOKENIZERS_PARALLELISM']='false' | |
| import numpy as np | |
| import mlx.core as mx | |
| from scipy.io import wavfile | |
| from scipy.signal import stft | |
| from mlx_audio.tts.utils import load_model | |
| ROOT=Path(os.environ.get('RESULTS_DIR', '.')) | |
| BASE=Path(os.environ['MODEL_ROOT']) | |
| variant=sys.argv[1] | |
| paths={ | |
| 'original':Path(os.environ['ORIGINAL_MODEL']), | |
| 'affine6':Path(os.environ['AFFINE6_MODEL']), | |
| } | |
| path=paths.get(variant,BASE/variant) | |
| mx.set_memory_limit(8*2**30);mx.set_cache_limit(128*2**20) | |
| model=load_model(str(path));model.eval();mx.eval(model.parameters());mx.synchronize() | |
| assert model.tokenizer is not None and model._conds is not None | |
| texts=[ | |
| 'This is the oMLX Chatterbox Turbo checkpoint. It was quantized from the original fp16 model.', | |
| 'Hello mate, the training checkpoint is saved, and this is Chatterbox speaking locally on the Mac.', | |
| 'Please check the appointment on Thursday at three thirty. Bring the blue folder, your keys, and a bottle of water.', | |
| ] | |
| def log_mel(audio, sr): | |
| _, _, z=stft(audio,fs=sr,nperseg=1024,noverlap=768,boundary='zeros') | |
| power=np.abs(z)**2 | |
| mel=lambda hz:2595*np.log10(1+hz/700) | |
| hz=lambda m:700*(10**(m/2595)-1) | |
| edges=hz(np.linspace(mel(40),mel(12000),82)) | |
| freqs=np.linspace(0,sr/2,power.shape[0]);bank=np.zeros((80,power.shape[0])) | |
| for j in range(80): | |
| bank[j]=np.maximum(0,np.minimum((freqs-edges[j])/(edges[j+1]-edges[j]),(edges[j+2]-freqs)/(edges[j+2]-edges[j+1]))) | |
| return bank@power | |
| rows=[] | |
| for i,text in enumerate(texts): | |
| tokens_file=ROOT/f'original-speech-tokens-{i}.npy' | |
| if variant=='original': | |
| capture={};original=model.s3gen.inference | |
| def traced(*args,**kwargs): | |
| capture['tokens']=np.array(kwargs['speech_tokens']) | |
| return original(*args,**kwargs) | |
| model.s3gen.inference=traced | |
| mx.random.seed(20261009);np.random.seed(20261009) | |
| for result in model.generate(text,temperature=.8,top_p=.95,top_k=1000,repetition_penalty=1.2,split_pattern=None,max_tokens=400):mx.eval(result.audio) | |
| model.s3gen.inference=original | |
| np.save(tokens_file,capture['tokens']) | |
| tokens=np.load(tokens_file) | |
| mx.random.seed(9182);mx.synchronize();start=time.perf_counter() | |
| audio,_=model.s3gen.inference(speech_tokens=mx.array(tokens),ref_dict=model._conds.gen,n_cfm_timesteps=2) | |
| mx.eval(audio);mx.synchronize();elapsed=time.perf_counter()-start | |
| audio=np.array(audio).reshape(-1).astype(np.float32) | |
| assert audio.size and np.isfinite(audio).all() | |
| np.save(ROOT/f'{variant}-decoder-{i}.npy',audio) | |
| wavfile.write(ROOT/f'{variant}-decoder-{i}.wav',24000,(np.clip(audio,-1,1)*32767).astype(np.int16)) | |
| reference=np.load(ROOT/f'original-decoder-{i}.npy') | |
| assert audio.shape==reference.shape,(audio.shape,reference.shape) | |
| a=log_mel(reference,24000);b=log_mel(audio,24000) | |
| floor=max(float(a.max())*1e-6,1e-12) | |
| active=(a>=max(float(a.max())*1e-4,1e-12)) | |
| delta=10*np.log10(np.maximum(a,floor))-10*np.log10(np.maximum(b,floor)) | |
| rms_delta=float(np.sqrt(np.mean(delta[active]**2))) | |
| row={'text':text,'speech_tokens':int(tokens.size),'audio_seconds':audio.size/24000,'decoder_seconds':elapsed,'log_mel_rms_difference_db':rms_delta,'peak_amplitude':float(np.abs(audio).max()),'clipped_fraction':float(np.mean(np.abs(audio)>=1))} | |
| rows.append(row);print('DECODER',variant,json.dumps(row),flush=True) | |
| report={'variant':variant,'method':'Identical original speech tokens, shared bundled conditioning, identical decoder RNG seed. RMS log-mel difference over reference bins above -40 dB, floor -60 dB. Lower means spectral output closer to original; not a subjective quality score.','decoder_seed':9182,'clips':rows,'mean_log_mel_rms_difference_db':float(np.mean([r['log_mel_rms_difference_db'] for r in rows]))} | |
| (ROOT/f'{variant}-decoder-fidelity.json').write_text(json.dumps(report,indent=2)+'\n') | |