From 52b723ef7d31e0990d59b467a49431c52596d776 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 22 Jan 2021 21:29:35 +0100 Subject: [PATCH] Add a migration script for Ardour --- tools/migration/README.md | 12 +++ tools/migration/migrate-ardour.py | 118 ++++++++++++++++++++++++++++++ tools/migration/migrate-bitwig.py | 18 ++--- tools/migration/migrate-reaper.py | 6 +- 4 files changed, 142 insertions(+), 12 deletions(-) create mode 100755 tools/migration/migrate-ardour.py diff --git a/tools/migration/README.md b/tools/migration/README.md index 38ee07f4..74280077 100644 --- a/tools/migration/README.md +++ b/tools/migration/README.md @@ -18,6 +18,18 @@ To use these, download the migration scripts for the DAWs you need to migrate an old project file for and run them. You'll get on screen instructions with what to do after that. +## Ardour + +```shell +# First download the script +curl -o migrate-ardour.py https://raw.githubusercontent.com/robbert-vdh/yabridge/master/tools/migration/migrate-ardour.py +chmod +x migrate-ardour.py + +# And then run it on any old .ardour files, the script will guide you through the +# migration process +./migrate-ardour.py /path/to/some/project/project.ardour +``` + ## Bitwig Studio ```shell diff --git a/tools/migration/migrate-ardour.py b/tools/migration/migrate-ardour.py new file mode 100755 index 00000000..d60c5f81 --- /dev/null +++ b/tools/migration/migrate-ardour.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import textwrap + + +# Parsing XML with regular expressions, what could go wrong! I'll make the +# assumption that the plugin developers were kind and did not include any +# double quotes in their plugin names, and that the order of attributes saved +# by Ardour is stable. This regexp will find VST3 plugins in a .ardour XML file +# and capture its name and class ID. +ARDOUR_VST3_RE = re.compile( + r'name="([^"]+)".+type="vst3".+unique-id="([0-9a-zA-Z]{32})"' +) + + +parser = argparse.ArgumentParser( + description="Migrate old yabridge VST3 plugin instances in Ardour project files." +) +parser.add_argument("filename", type=str, help="The .ardour project file to migrate.") + +# As a safety measure we want to limit the file names we accept +args = parser.parse_args() + +filename = args.filename +file_stem, file_extension = os.path.splitext(filename) +if file_extension.lower() != ".ardour": + print("For safety reasons, only '*.ardour' files are accepted") + exit(1) +if file_stem.endswith("-migrated"): + print("This project file has already been migrated to the new format") + exit(1) + +migrated_filename = file_stem + "-migrated" + file_extension +if os.path.exists(migrated_filename): + print( + f"'{migrated_filename}' already exists, back it up and move it elsewhere " + "if you want to redo the migration" + ) + exit(1) + +print( + "\n".join( + textwrap.wrap( + f"This script will go through '{filename}' to migrate old yabridge VST3 plugin instances. " + f"The output will be saved to '{migrated_filename}', but make sure to still create a backup of the original file in case something does go wrong. " + f"For every VST3 plugin found you will be prompted with the question if you want to migrate it. " + f"Answer 'yes' for all old yabridge VST3 plugin instances, and 'no' for any other VST3 plugin." + f"Make sure to test whether the new project works immediately after migration.", + width=80, + break_on_hyphens=False, + ) + ) +) +print() + +# We'll search through the original file, and prompt to replace all VST3 class +# IDs we come across. See `WineUID` in yabridge's source code for an +# explanation of this conversion. +with open(filename, "r", encoding="utf-8") as f_input, open( + migrated_filename, "x", encoding="utf-8" +) as f_output: + migrated_file = [] + for line in f_input.readlines(): + # Sadly can't use the walrus operator here since old distros might + # still ship with Python 3.6 + matches = ARDOUR_VST3_RE.search(line) + if matches is not None: + plugin_name = matches.group(1) + + wine_uid_start, wine_uid_end = matches.span(2) + wine_uid = bytearray.fromhex(matches.group(2)) + converted_uid = bytearray.fromhex(matches.group(2)) + + converted_uid[0] = wine_uid[3] + converted_uid[1] = wine_uid[2] + converted_uid[2] = wine_uid[1] + converted_uid[3] = wine_uid[0] + + converted_uid[4] = wine_uid[5] + converted_uid[5] = wine_uid[4] + converted_uid[6] = wine_uid[7] + converted_uid[7] = wine_uid[6] + + migrated_line = ( + line[:wine_uid_start] + + converted_uid.hex().upper() + + line[wine_uid_end:] + ) + + print(f"Found '{plugin_name}' with class ID '{wine_uid.hex().upper()}'") + while True: + answer = input("Should this plugin be migrated? [yes/no] ").lower() + if answer == "yes": + migrated_file.append(migrated_line) + break + elif answer == "no": + migrated_file.append(line) + break + else: + print("Please answer only 'yes' or 'no'") + else: + migrated_file.append(line) + + print(f"\nMigration successful, writing the results to '{migrated_filename}'") + f_output.writelines(migrated_file) + +print( + "\n".join( + textwrap.wrap( + f"You may have to manually clean Ardour's VST3 cache and rescan if it cannot find the plugins after migrating.", + width=80, + break_on_hyphens=False, + ) + ) +) diff --git a/tools/migration/migrate-bitwig.py b/tools/migration/migrate-bitwig.py index 88553137..7432b805 100755 --- a/tools/migration/migrate-bitwig.py +++ b/tools/migration/migrate-bitwig.py @@ -48,7 +48,7 @@ print( textwrap.wrap( f"This script will go through '{filename}' to migrate old yabridge VST3 plugin instances. " f"The output will be saved to '{migrated_filename}', but make sure to still create a backup of the original file in case something does go wrong. " - f"Migrating Bitwig project files is a two stop process. ", + "Migrating Bitwig project files is a two stop process. ", width=80, break_on_hyphens=False, ) @@ -59,9 +59,9 @@ print() print( "\n".join( textwrap.wrap( - f"First this script will rewrite the .bwproject file to use thew new plugin IDs. " - f"For every yabridge VST3 plugin found you will be prompted with the question if you want to migrate it. " - f"Answer 'yes' for all old yabridge VST3 plugin instances, and 'no' if this instance should not be migrated (for instance if you have a project file with mixed old and new instances). ", + "First this script will rewrite the .bwproject file to use thew new plugin IDs. " + "For every yabridge VST3 plugin found you will be prompted with the question if you want to migrate it. " + "Answer 'yes' for all old yabridge VST3 plugin instances, and 'no' if this instance should not be migrated (for instance if you have a project file with mixed old and new instances). ", width=80, break_on_hyphens=False, ) @@ -73,11 +73,11 @@ print( "\n".join( textwrap.wrap( f"After that you will be asked to open the new '{migrated_filename}' project. " - f"During this process you should make that all other Bitwig projects are closed. " - f"When opening the new project you will notice that the migrated plugins will try to load but then fail because they cannot load their preset files. " - f"At this point you should tell this script to continue, and it will rewrite the preset files. " - f"If you then save and reopen the project, everything should work again. " - f"Make sure to test whether the new project works immediately after finishing this migration process.", + "During this process you should make that all other Bitwig projects are closed. " + "When opening the new project you will notice that the migrated plugins will try to load but then fail because they cannot load their preset files. " + "At this point you should tell this script to continue, and it will rewrite the preset files. " + "If you then save and reopen the project, everything should work again. " + "Make sure to test whether the new project works immediately after finishing this migration process.", width=80, break_on_hyphens=False, ) diff --git a/tools/migration/migrate-reaper.py b/tools/migration/migrate-reaper.py index c380c0e9..e9404ad0 100755 --- a/tools/migration/migrate-reaper.py +++ b/tools/migration/migrate-reaper.py @@ -44,9 +44,9 @@ print( textwrap.wrap( f"This script will go through '{filename}' to migrate old yabridge VST3 plugin instances. " f"The output will be saved to '{migrated_filename}', but make sure to still create a backup of the original file in case something does go wrong. " - f"For every VST3 plugin found you will be prompted with the question if you want to migrate it. " - f"Answer 'yes' for all old yabridge VST3 plugin instances, and 'no' for any other VST3 plugin." - f"Make sure to test whether the new project works immediately after migration.", + "For every VST3 plugin found you will be prompted with the question if you want to migrate it. " + "Answer 'yes' for all old yabridge VST3 plugin instances, and 'no' for any other VST3 plugin." + "Make sure to test whether the new project works immediately after migration.", width=80, break_on_hyphens=False, )