2023-11-17 00:31:41 +05:30
|
|
|
#!/usr/bin/python3
|
|
|
|
|
2023-11-17 00:59:52 +05:30
|
|
|
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
|
2023-11-17 00:31:41 +05:30
|
|
|
DEFAULT_PARAMETERS = {
|
|
|
|
"CC": "gcc",
|
2023-11-21 21:27:02 +05:30
|
|
|
"CC_FLAGS": "-Os -Wall -Wextra -Wpedantic",
|
2023-11-17 00:59:52 +05:30
|
|
|
"INPUT_FILE": PATHS["substitute"],
|
|
|
|
"OUTPUT_FILE": PATHS["output"]
|
2023-11-17 00:31:41 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2023-11-18 17:49:07 +05:30
|
|
|
def substitute_var(placeholder: str, replacement, text: str) -> str:
|
2023-11-17 00:31:41 +05:30
|
|
|
return text.replace(f"`{placeholder}`", str(replacement))
|
|
|
|
|
|
|
|
CC = fetch("CC")
|
|
|
|
CC_FLAGS = fetch("CC_FLAGS")
|
2023-11-17 00:59:52 +05:30
|
|
|
INPUT_FILE = fetch("INPUT_FILE")
|
2023-11-17 00:31:41 +05:30
|
|
|
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 "
|
2023-11-17 00:59:52 +05:30
|
|
|
"environmental variables `CC`, `CC_FLAGS`, `INPUT_FILE`, "
|
2023-11-17 00:31:41 +05:30
|
|
|
"`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)")
|
2023-11-18 16:45:48 +05:30
|
|
|
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)")
|
2023-11-17 00:31:41 +05:30
|
|
|
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
|
|
|
|
|
2023-11-17 00:59:52 +05:30
|
|
|
# - Compilation
|
|
|
|
makedirs(PATHS["build_dir"], exist_ok=True)
|
|
|
|
|
2023-11-17 00:31:41 +05:30
|
|
|
if not args.no_return: # Insert return statement
|
|
|
|
bytebeat_contents = f"\treturn\n\n{bytebeat_contents}"
|
|
|
|
|
2023-11-18 16:45:48 +05:30
|
|
|
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
|
|
|
|
|
2023-11-17 00:59:52 +05:30
|
|
|
substitute = read_file(PATHS["template"])
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("bytebeat_contents",
|
2023-11-17 00:59:52 +05:30
|
|
|
bytebeat_contents, substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("sample_rate",
|
2023-11-17 00:59:52 +05:30
|
|
|
args.sample_rate, substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("final_sample_rate_code",
|
2023-11-18 16:45:48 +05:30
|
|
|
final_sample_rate_code, substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("bit_depth",
|
2023-11-17 00:59:52 +05:30
|
|
|
args.bit_depth, substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("is_signed",
|
2023-11-17 00:59:52 +05:30
|
|
|
"1" if args.signed else "0", substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("channels",
|
2023-11-17 00:59:52 +05:30
|
|
|
args.channels, substitute)
|
2023-11-18 17:49:07 +05:30
|
|
|
substitute = substitute_var("seconds",
|
2023-11-17 00:59:52 +05:30
|
|
|
args.seconds, substitute)
|
|
|
|
rewrite_file(PATHS["substitute"], substitute)
|
2023-11-17 00:31:41 +05:30
|
|
|
|
|
|
|
# Compile by invoking the shell script
|
|
|
|
print("Compiling")
|
|
|
|
|
2023-11-17 00:59:52 +05:30
|
|
|
# Let the system execute aliases by calling os.system
|
|
|
|
system(" ".join([CC, CC_FLAGS, INPUT_FILE, "-o", OUTPUT_FILE]))
|