#!/usr/bin/python3 from os.path import exists, join as path_join from os import system, environ, makedirs from argparse import ArgumentParser from sys import stdin import subprocess # Paths PATHS = { "src_dir": "src/", "build_dir": "build/", "template": "template.c", "substitute": "substituted.c", "output": "render_bytebeat" } # Solve paths PATHS["template"] = path_join(PATHS["src_dir"], PATHS["template"]) PATHS["substitute"] = path_join(PATHS["build_dir"], PATHS["substitute"]) PATHS["output"] = path_join(PATHS["build_dir"], PATHS["output"]) # Default parameters DEFAULT_PARAMETERS = { "CC": "gcc", "CC_FLAGS": "-Os -Wall -Wextra -Wpedantic", "INPUT_FILE": PATHS["substitute"], "OUTPUT_FILE": PATHS["output"] } def fetch(name: str): return res if (res := environ.get(name)) else DEFAULT_PARAMETERS[name] def read_file(path: str) -> str: return open(path, "r", encoding="utf-8-sig").read() def rewrite_file(path: str, content: str): return open(path, "w", encoding="utf-8-sig").write(content) def read_from_file_or_stdin(path: str) -> str: if path == "-": return "\n".join(stdin) elif exists(path): return read_file(path) else: print("The specified file doesn't exist") raise SystemExit def substitute_var(placeholder: str, replacement, text: str) -> str: return text.replace(f"`{placeholder}`", str(replacement)) CC = fetch("CC") CC_FLAGS = fetch("CC_FLAGS") INPUT_FILE = fetch("INPUT_FILE") OUTPUT_FILE = fetch("OUTPUT_FILE") if __name__ == "__main__": parser = ArgumentParser(description=\ "Substitutes supplied C (non-JavaScript!) bytebeat into the template, " "then attempts to compile the instance of the template. Uses " "environmental variables `CC`, `CC_FLAGS`, `INPUT_FILE`, " "`OUTPUT_FILE`.") parser.add_argument("file", type=str, help="bytebeat formula file") parser.add_argument("-r", "--sample-rate", default=8000, type=int, help="sample rate (Hz)") parser.add_argument("-p", "--final-sample-rate", default=None, type=int, help="convert the output to a different sample rate (usually one that " "is set in the system, to improve sound quality) during generation " "(not just reinterpretation)") parser.add_argument("-b", "--bit-depth", default=8, type=int, help="bit depth") parser.add_argument("-s", "--signed", default=False, action="store_true", help="is signed?") parser.add_argument("-c", "--channels", default=1, type=int, help="amount of channels") parser.add_argument("-t", "--seconds", default=30, type=int, help="length (seconds)") parser.add_argument("-a", "--no-return", default=False, action="store_true", help="do not insert return statement before the code") args = parser.parse_args() bytebeat_contents = read_from_file_or_stdin(args.file).strip() if not bytebeat_contents: print("No valid contents") raise SystemExit # - Compilation makedirs(PATHS["build_dir"], exist_ok=True) if not args.no_return: # Insert return statement bytebeat_contents = f"\treturn\n\n{bytebeat_contents}" final_sample_rate_code = "" if not args.final_sample_rate is None: sample_rate_ratio = args.sample_rate / args.final_sample_rate final_sample_rate_code = f"w *= {sample_rate_ratio}L;" args.sample_rate = args.final_sample_rate substitute = read_file(PATHS["template"]) substitute = substitute_var("bytebeat_contents", bytebeat_contents, substitute) substitute = substitute_var("sample_rate", args.sample_rate, substitute) substitute = substitute_var("final_sample_rate_code", final_sample_rate_code, substitute) substitute = substitute_var("bit_depth", args.bit_depth, substitute) substitute = substitute_var("is_signed", "1" if args.signed else "0", substitute) substitute = substitute_var("channels", args.channels, substitute) substitute = substitute_var("seconds", args.seconds, substitute) rewrite_file(PATHS["substitute"], substitute) # Compile by invoking the shell script print("Compiling") # Let the system execute aliases by calling os.system system(" ".join([CC, CC_FLAGS, INPUT_FILE, "-o", OUTPUT_FILE]))