Skip to content

Instantly share code, notes, and snippets.

@anytizer
Last active October 26, 2025 03:23
Show Gist options
  • Select an option

  • Save anytizer/18068e9fe0461767406353bb02c3cf58 to your computer and use it in GitHub Desktop.

Select an option

Save anytizer/18068e9fe0461767406353bb02c3cf58 to your computer and use it in GitHub Desktop.
LMMS ZynAddSubFx presets analysis
# Author: @anytizer, 2025-10-25
# Advanced usages:
# python lmms-zyn.py > lmms-zyn.csv
# Helps to analyze zynaddsubfx presets (compressed files)
# Compare with original release repo and LMMS Embedded Presets
import os
import hashlib
import zlib, gzip
from pathlib import Path
import xml.etree.ElementTree as ET
from bs4 import BeautifulSoup, XMLParsedAsHTMLWarning
import warnings
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
# Obtain whole list of .xiz files
def find_xiz_files(folder_path):
folder = Path(folder_path)
xiz_files = list(folder.rglob("*.xiz"))
return xiz_files
# A CRC signature of a file
def crc32_file(filename, chunk_size=65536):
checksum = 0
with open(filename, 'rb') as f:
while chunk := f.read(chunk_size):
checksum = zlib.crc32(chunk, checksum)
return hex(checksum & 0xFFFFFFFF)
# Pick a file signature
def xiz_signature(filename="*.xiz"):
return crc32_file(filename)
# Convert bytes into human readable form
def human_readable_size(size_bytes=0):
units = ["B", "KB", "MB", "GB", "TB", "PB"]
i = 0
while size_bytes >= 1024 and i < len(units) - 1:
size_bytes /= 1024.0
i += 1
return f"{size_bytes:.2f} {units[i]}"
# Decompressed XML Content from .xiz file
def xiz_fc(filename="*.xiz"):
content = ""
try:
with gzip.open(filename, 'rt', encoding='utf-8') as f:
content = f.read()
except Exception as e:
content = "<ZynAddSubFX-data ZynAddSubFX-author='______'></ZynAddSubFX-data>"
return content
# Obtain file format author name
# <ZynAddSubFX-data version-revision="1" version-minor="4" ZynAddSubFX-author="_______" version-major="2">
def xiz_author1(xml_content="*.xiz"):
author = "Default Author Name"
try:
root = BeautifulSoup(xml_content, "lxml")
ZynAddSubFX = root.find_all("ZynAddSubFX-data")
author = ZynAddSubFX.get("ZynAddSubFX-author")
except Exception as e:
author = "Author Not Found"
return author
# Obtain instrument author
def xiz_author2(xml_content="*.xiz"):
author = "Instrument Author"
try:
root = ET.fromstring(xml_content)
author_element = root.find(".//string[@name='author']")
author = ""+author_element.text if author_element is not None else ""
except Exception as e:
author = "Author Not Found"
return author
# Convert file details into static hash
def special_signature(author, instrument, basename=""):
fullname = f"{author}/{instrument}/{basename}"
md5_hash = hashlib.md5()
md5_hash.update(fullname.encode("utf-8"))
hex_digest = md5_hash.hexdigest().upper()
return hex_digest
# Write an uncompressed XML file
def write_xml(filename="", xml_content=""):
with open(filename, "w", encoding="latin-1") as fw:
fw.write(xml_content)
fw.close()
# Calculate variables
def analyze(filename="*.xiz"):
size = human_readable_size(os.path.getsize(filename))
instrument = os.path.basename(os.path.dirname(filename))
basename = os.path.basename(filename)
signature = xiz_signature(filename)
return [instrument, basename, size, signature]
# Some scripts to run early
def setup():
# Empty the xmls files if any
# Remove the xmls/ folder
# Reduce disk i/o
os.makedirs("xmls", exist_ok=True)
return True
def collect_xiz_files():
# Path to instrument presets from one of the folders when checked out
# - https://github.com/LMMS/lmms/tree/master/data/presets/ZynAddSubFX
# - https://github.com/zynaddsubfx/instruments/tree/master/banks
#folder_to_scan = "lmms/data/presets/ZynAddSubFX"
folder_to_scan = "instruments/banks"
return find_xiz_files(folder_to_scan)
# usage
if __name__ == "__main__":
operated = setup()
xiz_files = collect_xiz_files()
counter = 0
for filename in xiz_files:
counter += 1
xml_content = xiz_fc(filename)
instrument, basename, size, signature = analyze(filename)
author1 = xiz_author1(xml_content) # file format author
author2 = xiz_author2(xml_content) # instrument author
author = author2
# Write uncompresed xml
#new_filename = "xmls/"+special_signature(author, instrument, basename)+".xml"
#write_xml(new_filename, xml_content)
print(
f'{counter:05}',
f"{instrument:30}",
f"{basename:50}",
f"{size:15}",
f"{signature:10}",
f"{author:30}",
sep=" | "
)
# break
# End of file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment