From f1747fbb4926d5f26be404901de9899429517679 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 21:41:15 -0400 Subject: [PATCH 01/30] Add files via upload --- melo/ConvertAudiotoWav.bat | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 melo/ConvertAudiotoWav.bat diff --git a/melo/ConvertAudiotoWav.bat b/melo/ConvertAudiotoWav.bat new file mode 100644 index 000000000..dc2636e47 --- /dev/null +++ b/melo/ConvertAudiotoWav.bat @@ -0,0 +1,46 @@ +@echo off +setlocal enabledelayedexpansion + +rem Specify input and output folders here +set "INPUT_FOLDER=data\example\audios" +set "OUTPUT_FOLDER=data\example\wavs" + +rem Create output folder if it doesn't exist +if not exist "%OUTPUT_FOLDER%" mkdir "%OUTPUT_FOLDER%" + +rem Initialize counter for sequential naming +set /a counter=1 + +rem Loop through common audio formats that ffmpeg supports +for %%F in ( + "%INPUT_FOLDER%\*.mp3" + "%INPUT_FOLDER%\*.m4a" + "%INPUT_FOLDER%\*.wav" + "%INPUT_FOLDER%\*.ogg" + "%INPUT_FOLDER%\*.flac" + "%INPUT_FOLDER%\*.aac" + "%INPUT_FOLDER%\*.wma" + "%INPUT_FOLDER%\*.aiff" + "%INPUT_FOLDER%\*.aifc" + "%INPUT_FOLDER%\*.opus" + "%INPUT_FOLDER%\*.ape" + "%INPUT_FOLDER%\*.wv" + "%INPUT_FOLDER%\*.m4b" + "%INPUT_FOLDER%\*.mp2" + "%INPUT_FOLDER%\*.mp4" + "%INPUT_FOLDER%\*.mpc" + "%INPUT_FOLDER%\*.mka" + "%INPUT_FOLDER%\*.ac3" + "%INPUT_FOLDER%\*.dts" + "%INPUT_FOLDER%\*.amr" + "%INPUT_FOLDER%\*.au" + "%INPUT_FOLDER%\*.mid" +) do ( + rem Convert the file using ffmpeg + ffmpeg -i "%%F" -acodec pcm_s16le -ar 44100 "%OUTPUT_FOLDER%\!counter!.wav" + + rem Increment the counter + set /a counter+=1 +) + +echo Conversion complete. Check the '%OUTPUT_FOLDER%' folder for the converted files. \ No newline at end of file From c7b87d1bdbc53fb602214cb98d9fe7ea442e36b7 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:05:53 -0400 Subject: [PATCH 02/30] Add files via upload --- melo/trim_model.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 melo/trim_model.py diff --git a/melo/trim_model.py b/melo/trim_model.py new file mode 100644 index 000000000..7c9b3b085 --- /dev/null +++ b/melo/trim_model.py @@ -0,0 +1,82 @@ +import os +import torch +import argparse +from pathlib import Path + +def clean_checkpoint(input_path, output_path=None): + """ + Convert a full MeloTTS training checkpoint to an inference-only checkpoint. + + Args: + input_path (str): Path to the full checkpoint + output_path (str, optional): Path for the clean checkpoint + """ + print(f"Loading checkpoint: {input_path}") + + # Load the checkpoint + checkpoint = torch.load(input_path, map_location='cpu') + + print("\nCheckpoint contents:") + print("Keys in checkpoint:", list(checkpoint.keys())) + + # Create clean checkpoint keeping only necessary components + clean_checkpoint = { + 'model': checkpoint['model'], # Keep the model state + 'iteration': checkpoint['iteration'], # Keep track of training iteration + 'learning_rate': checkpoint['learning_rate'], # Keep the learning rate + # Exclude the optimizer state + } + + # Generate output path if not provided + if output_path is None: + input_path = Path(input_path) + output_path = input_path.parent / f"{input_path.stem}_clean{input_path.suffix}" + + # Save the clean checkpoint + print(f"\nSaving clean checkpoint to: {output_path}") + torch.save(clean_checkpoint, output_path) + + # Print size comparison + original_size = os.path.getsize(input_path) / (1024 * 1024) # MB + clean_size = os.path.getsize(output_path) / (1024 * 1024) # MB + + print(f"\nSize comparison:") + print(f"Original checkpoint: {original_size:.2f} MB") + print(f"Clean checkpoint: {clean_size:.2f} MB") + print(f"Size reduction: {((original_size - clean_size) / original_size * 100):.1f}%") + + # Verify the clean checkpoint was saved properly + if os.path.getsize(output_path) == 0: + raise Exception("Error: Output file is empty!") + + # Load the clean checkpoint to verify it's valid + try: + test_load = torch.load(output_path, map_location='cpu') + print("\nVerification successful - cleaned checkpoint contains:") + print("Keys in cleaned checkpoint:", list(test_load.keys())) + except Exception as e: + raise Exception(f"Error verifying cleaned checkpoint: {str(e)}") + +def main(): + parser = argparse.ArgumentParser(description='Convert MeloTTS training checkpoint to inference-only checkpoint') + parser.add_argument('input_path', type=str, help='Path to the full checkpoint file') + parser.add_argument('--output_path', type=str, default=None, + help='Path for the clean checkpoint (optional)') + parser.add_argument('--debug', action='store_true', + help='Print additional debugging information') + + args = parser.parse_args() + + try: + clean_checkpoint(args.input_path, args.output_path) + print("\nCheckpoint cleaned successfully!") + except Exception as e: + print(f"\nError occurred: {str(e)}") + import traceback + traceback.print_exc() + return 1 + + return 0 + +if __name__ == "__main__": + main() \ No newline at end of file From 7d1ea7782a1badf08cc5a9764bb443ac019531cf Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:09:33 -0400 Subject: [PATCH 03/30] Add files via upload --- melo/trim_model.bat | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 melo/trim_model.bat diff --git a/melo/trim_model.bat b/melo/trim_model.bat new file mode 100644 index 000000000..771b0f3d8 --- /dev/null +++ b/melo/trim_model.bat @@ -0,0 +1,3 @@ +conda activate melotts-win + +python trim_model.py "Path\To\Model\G_xxxx.pth" --output_path "Path\To\Save\Model\G_xxxx_cleaned.pth" \ No newline at end of file From c7259b9e0361b04dd3308004a1d18e27e8335469 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:12:56 -0400 Subject: [PATCH 04/30] Update preprocess_text.py adjust default settings for training/resume training and config file creation --- melo/preprocess_text.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/melo/preprocess_text.py b/melo/preprocess_text.py index 8fdf87295..5e01ff81e 100644 --- a/melo/preprocess_text.py +++ b/melo/preprocess_text.py @@ -149,10 +149,16 @@ def main( config["num_tones"] = num_tones config["symbols"] = symbols + # Added lines: + config["train"]["skip_optimizer"] = False # Ensure skip_optimizer is False by default. Seems this must be false to be able to resume training later. + config["train"]["n_ckpts_to_keep"] = 5 # Add checkpoint keeping parameter + config["train"]["eval_interval"] = 200 # How often checkpoints are saved. Set default eval_interval to 200, removing this line will default it to 1000 + config["train"]["batch_size"] = 12 # Set default batch_size to 12, removing this line will default it to 6 + with open(out_config_path, "w", encoding="utf-8") as f: json.dump(config, f, indent=2, ensure_ascii=False) logger.info("Preprocessing completed successfully") if __name__ == "__main__": - main() \ No newline at end of file + main() From 31d770c388f53b62e6126b0f93e4724c00d05f2c Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:16:17 -0400 Subject: [PATCH 05/30] Update transcript.py default to "medium" whisper model (for balance between speed and quality), as "base" is good and faster but tends to have more transcription mistakes. --- melo/transcript.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/melo/transcript.py b/melo/transcript.py index f17f02809..8111eaf7a 100644 --- a/melo/transcript.py +++ b/melo/transcript.py @@ -8,7 +8,7 @@ LANGUAGE_MODEL = "EN-default" # Load the whisper model -model = whisper.load_model("base") +model = whisper.load_model("medium") # Get the list of WAV files in the input directory wav_files = [file for file in os.listdir(INPUT_FOLDER) if file.endswith(".wav")] @@ -37,4 +37,4 @@ line += "\n" transcript_file.write(line) -print(f"Transcription complete. Check '{OUTPUT_FILE}' for results.") \ No newline at end of file +print(f"Transcription complete. Check '{OUTPUT_FILE}' for results.") From 896be6e501923e17584930c9ac623adf746ea84a Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:39:09 -0400 Subject: [PATCH 06/30] Add files via upload --- melo/transcript_fast.py | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 melo/transcript_fast.py diff --git a/melo/transcript_fast.py b/melo/transcript_fast.py new file mode 100644 index 000000000..bb7797694 --- /dev/null +++ b/melo/transcript_fast.py @@ -0,0 +1,42 @@ +import os +os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE' +from faster_whisper import WhisperModel + +# Configuration variables +INPUT_FOLDER = "data\\example\\wavs" +OUTPUT_FILE = "data\\example\\metadata.list" +LANGUAGE_CODE = "EN" +LANGUAGE_MODEL = "EN-default" + +# Initialize the faster-whisper model +model = WhisperModel("medium", device="cuda", compute_type="int8_float16") + +# Get the list of WAV files in the input directory +wav_files = [file for file in os.listdir(INPUT_FOLDER) if file.endswith(".wav")] + +# Sort the WAV files in numeric order +wav_files = sorted(wav_files, key=lambda x: int(os.path.splitext(x)[0])) + +# Open a text file for writing the transcripts +with open(OUTPUT_FILE, "w", encoding="utf-8") as transcript_file: + # Prepare the output path with forward slashes + output_path = INPUT_FOLDER.replace('\\', '/') + + # Iterate through each WAV file + for i, wav_file in enumerate(wav_files): + print(f"Transcribing: {wav_file}") + # Construct the full path to the WAV file + wav_path = os.path.join(INPUT_FOLDER, wav_file) + + # Transcribe using faster-whisper + segments, info = model.transcribe(wav_path) + # Get the full text from all segments + transcribed_text = " ".join([segment.text for segment in segments]).strip() + + # Write the result to the transcript file in the MeloTTS format + line = f"{output_path}/{wav_file}|{LANGUAGE_MODEL}|{LANGUAGE_CODE}|{transcribed_text}" + if i < len(wav_files) - 1: + line += "\n" + transcript_file.write(line) + +print(f"Transcription complete. Check '{OUTPUT_FILE}' for results.") \ No newline at end of file From bc854b1c4658eff35fb52c54b338b50211372308 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:53:44 -0400 Subject: [PATCH 07/30] Update transcript_fast.py add cmd arguments --- melo/transcript_fast.py | 79 ++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/melo/transcript_fast.py b/melo/transcript_fast.py index bb7797694..6d253b094 100644 --- a/melo/transcript_fast.py +++ b/melo/transcript_fast.py @@ -1,42 +1,57 @@ import os +import argparse os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE' from faster_whisper import WhisperModel -# Configuration variables -INPUT_FOLDER = "data\\example\\wavs" -OUTPUT_FILE = "data\\example\\metadata.list" -LANGUAGE_CODE = "EN" -LANGUAGE_MODEL = "EN-default" +def transcribe_folder(input_folder, output_file): + LANGUAGE_CODE = "EN" + LANGUAGE_MODEL = "EN-default" -# Initialize the faster-whisper model -model = WhisperModel("medium", device="cuda", compute_type="int8_float16") + # Initialize the faster-whisper model + model = WhisperModel("medium", device="cuda", compute_type="int8_float16") -# Get the list of WAV files in the input directory -wav_files = [file for file in os.listdir(INPUT_FOLDER) if file.endswith(".wav")] + # Get the list of WAV files in the input directory + wav_files = [file for file in os.listdir(input_folder) if file.endswith(".wav")] -# Sort the WAV files in numeric order -wav_files = sorted(wav_files, key=lambda x: int(os.path.splitext(x)[0])) + # Sort the WAV files in numeric order + wav_files = sorted(wav_files, key=lambda x: int(os.path.splitext(x)[0])) -# Open a text file for writing the transcripts -with open(OUTPUT_FILE, "w", encoding="utf-8") as transcript_file: - # Prepare the output path with forward slashes - output_path = INPUT_FOLDER.replace('\\', '/') - - # Iterate through each WAV file - for i, wav_file in enumerate(wav_files): - print(f"Transcribing: {wav_file}") - # Construct the full path to the WAV file - wav_path = os.path.join(INPUT_FOLDER, wav_file) - - # Transcribe using faster-whisper - segments, info = model.transcribe(wav_path) - # Get the full text from all segments - transcribed_text = " ".join([segment.text for segment in segments]).strip() + # Open a text file for writing the transcripts + with open(output_file, "w", encoding="utf-8") as transcript_file: + # Prepare the output path with forward slashes + output_path = input_folder.replace('\\', '/') - # Write the result to the transcript file in the MeloTTS format - line = f"{output_path}/{wav_file}|{LANGUAGE_MODEL}|{LANGUAGE_CODE}|{transcribed_text}" - if i < len(wav_files) - 1: - line += "\n" - transcript_file.write(line) + # Iterate through each WAV file + for i, wav_file in enumerate(wav_files): + print(f"Transcribing: {wav_file}") + wav_path = os.path.join(input_folder, wav_file) + + segments, info = model.transcribe(wav_path) + transcribed_text = " ".join([segment.text for segment in segments]).strip() + + line = f"{output_path}/{wav_file}|{LANGUAGE_MODEL}|{LANGUAGE_CODE}|{transcribed_text}" + if i < len(wav_files) - 1: + line += "\n" + transcript_file.write(line) -print(f"Transcription complete. Check '{OUTPUT_FILE}' for results.") \ No newline at end of file + print(f"Transcription complete. Check '{output_file}' for results.") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Transcribe WAV files using faster-whisper') + parser.add_argument('--wavs_path', type=str, default="data\\example\\wavs", + help='Path to the directory containing WAV files (default: data\\example\\wavs)') + parser.add_argument('--metadata_path', type=str, default="data\\example\\metadata.list", + help='Path for the output metadata.list file (default: data\\example\\metadata.list)') + + args = parser.parse_args() + + # Check if the input directory exists + if not os.path.exists(args.wavs_path): + print(f"Error: Directory '{args.wavs_path}' does not exist.") + exit(1) + + # Create output directory if it doesn't exist + os.makedirs(os.path.dirname(args.metadata_path), exist_ok=True) + + # Run transcription + transcribe_folder(args.wavs_path, args.metadata_path) From bea99313516ce33ca662d76853d36d646b1a0791 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:57:00 -0400 Subject: [PATCH 08/30] Add files via upload --- melo/transcript_fast.bat | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 melo/transcript_fast.bat diff --git a/melo/transcript_fast.bat b/melo/transcript_fast.bat new file mode 100644 index 000000000..8bc6fd23a --- /dev/null +++ b/melo/transcript_fast.bat @@ -0,0 +1,4 @@ +conda activate melotts-win + +python transcript_fast.py + From 739d25b89cc6b9555e90ad57d76ceafe71638beb Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:58:40 -0400 Subject: [PATCH 09/30] Update requirements.txt added faster-whisper --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6df4ce360..c674afb69 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ langid==1.1.6 tqdm tensorboard==2.16.2 loguru==0.7.2 +faster-whisper==0.9.0 From 35dd0c07cce82cbe7cfdd835925ad63c8353bb9b Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:06:59 -0400 Subject: [PATCH 10/30] Update ConvertAudiotoWav.bat --- melo/ConvertAudiotoWav.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/melo/ConvertAudiotoWav.bat b/melo/ConvertAudiotoWav.bat index dc2636e47..ca893831c 100644 --- a/melo/ConvertAudiotoWav.bat +++ b/melo/ConvertAudiotoWav.bat @@ -2,7 +2,7 @@ setlocal enabledelayedexpansion rem Specify input and output folders here -set "INPUT_FOLDER=data\example\audios" +set "INPUT_FOLDER=data\example\audio" set "OUTPUT_FOLDER=data\example\wavs" rem Create output folder if it doesn't exist @@ -43,4 +43,4 @@ for %%F in ( set /a counter+=1 ) -echo Conversion complete. Check the '%OUTPUT_FOLDER%' folder for the converted files. \ No newline at end of file +echo Conversion complete. Check the '%OUTPUT_FOLDER%' folder for the converted files. From 7350be03e7da73654a00220531245cb958d37dc9 Mon Sep 17 00:00:00 2001 From: EliseWindbloom <109670213+EliseWindbloom@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:38:44 -0400 Subject: [PATCH 11/30] Update README.md --- README.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 05f19e9c2..28214871a 100644 --- a/README.md +++ b/README.md @@ -26,25 +26,38 @@ If you have trouble doing the download with the `python -m unidic download` you pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 ``` +3. Prepare faster-whisper (optional for fast transcribing of audio files): + - If you have cuda/cublas errors, download this `https://github.com/Purfview/whisper-standalone-win/releases/download/libs/cuBLAS.and.cuDNN_CUDA11_win_v2.7z`, extract and place the 5 dll files directly into the `MeloTTS-Windows/melo/` folder + 4. Run using: ``` melo-ui ``` # Local Training on Windows +## Preparing Dataset 1. In the `melo/data/example` folder, delete the example `metadata.list` file. -2. If you need to convert mp3 to wav, create a folder called `mp3s` in the example folder and copy all your mp3 files into the `mp3s` folder -3. With a conda window activated with the enviroment open in the `melo` folder, run `ConvertMp3toWav.bat` from the conda prompt. This will create a folder `data/example/wavs` with all of the converted wav files. -4. Create a transcript file by running `python transcript.py` which will create a `data/example/metadata.list` file. -5. Run `python preprocess_text.py --metadata data/example/metadata.list` to create the `train.list`, `config.json`, among other files in the `data/example` folder. -6. Modify `config.json` to change the batch size, epochs, learning rate, etc. -7. From the conda prompt run `train.bat` to start the training. -8. File will be created within the `data/example/config` folder with the checkpoints and other logging information. -9. To test out a checkpoint, run: `python infer.py --text "this is a test" -m "C:\ai\MeloTTS-Windows\melo\data\example\config\G_0.pth" -o output` changing the G_0 to the checkpoint you want to test with G_1000, G2000, etc. -10. When you want to use a checkpoint from the UI, create a `melo/custom` folder and copy the .pth and `config.json` file over from the `data/example/config`, rename the .pth to a user-friendly name, and launch the UI to see it in the custom voice dropdown. -11. To see the tensorboard, install `pip install tensorflow` -12. Run `tensorboard --logdir=data\example\config` -13. This will give you the local URL to view the tensorboard. +2. MeloTTS expects wav audio files (with a sample rate of 44100Hz). If you need to convert audio to wav format (with 44100Hz sample rate), create a folder called `audio` in the example folder and copy all your audio files into the `audio` folder +4. With a conda window activated with the enviroment open in the `melo` folder, run `ConvertAudiotoWav.bat` from the conda prompt. This will create a folder `data/example/wavs` with all of the converted wav files. +5. Create a transcript file by running `transcript_fast.bat` which will create a `data/example/metadata.list` file using faster-whisper. Alternately, you can run `python transcript.py` to use the original whisper. +6. Run `python preprocess_text.py --metadata data/example/metadata.list` to create the `train.list`, `config.json`, among other files in the `data/example` folder. +7. Modify `config.json` to change the batch size, epochs, learning rate, etc. + - ⚠️ **Important, If you plan to Resume Training Later:** + - The `eval_interval` setting determines how frequently your model is saved during training + - For example, if `eval_interval=1000`, the model saves only once every 1000 epochs + - If you stop training between save points, any progress since the last save will be lost + - For safer training sessions that you may need to resume later, use a smaller `eval_interval` value + - You can also adjust `n_ckpts_to_keep` to limit the max models kept (if `n_ckpts_to_keep=5`, it will delete the oldest models when their are more than 5 saved models) +## Start Training +1. From the conda prompt run `train.bat` to start the training. +2. File will be created within the `data/example/config` folder with the checkpoints and other logging information. +3. To test out a checkpoint, run: `python infer.py --text "this is a test" -m "C:\ai\MeloTTS-Windows\melo\data\example\config\G_0.pth" -o output` changing the G_0 to the checkpoint you want to test with G_1000, G2000, etc. +4. When you want to use a checkpoint from the UI, create a `melo/custom` folder and copy the .pth and `config.json` file over from the `data/example/config`, rename the .pth to a user-friendly name, and launch the UI to see it in the custom voice dropdown. +5. To see the tensorboard, install `pip install tensorflow` +6. Run `tensorboard --logdir=data\example\config` +7. This will give you the local URL to view the tensorboard. +## Resuming Training +1. From the conda prompt run `train.bat` again to resume the training. The training will resume from the newest G_XXXX.pth file. # Original Readme: