merge from WIP
This commit is contained in:
commit
c3b470f842
@ -1,9 +1,12 @@
|
||||
## Example usage
|
||||
|
||||
```text
|
||||
$ echo 't&((t>>7)-t)&t>>8' | python ./bytebeat_compiler.py - -p 44100 -v
|
||||
$ echo 't&((t>>7)-t)&t>>8' | python ./bytebeat_compiler.py - -p 44100 --verbose
|
||||
:: C bytebeat generator: compiler unit
|
||||
Reading from STDIN...
|
||||
Compiling
|
||||
cc -Ofast -march=native -mtune=native -Wall -Wextra -Wpedantic -pedantic -Wno-unused-variable -Wno-unused-but-set-variable -Wno-dangling-else -Wno-parentheses -std=c99 ./bin/substituted.c ./src/fwrite_le.c -o ./bin/render_bytebeat -I./include
|
||||
./bin/render_bytebeat
|
||||
:: C bytebeat generator runtime unit
|
||||
|
||||
Sample rate: 44100 Hz
|
||||
@ -11,9 +14,7 @@ Channels: 1 (mono)
|
||||
Bit depth: unsigned 8-bit
|
||||
Duration: 30 seconds
|
||||
|
||||
remaining samples = 0 (100.00% done)
|
||||
Writing out file output.wav...
|
||||
Done!
|
||||
Writing WAVE headers...
|
||||
|
||||
$
|
||||
Done!
|
||||
```
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
## License
|
||||
|
||||
Dual-licensed under the [Unlicense](http://unlicense.org)
|
||||
([`LICENSE`](LICENSE)) and Creative Commons Zero 1.0 Universal
|
||||
([`COPYING`](COPYING)).
|
||||
Dual-licensed under the [Creative Commons Zero 1.0 Universal][CC0-1.0]
|
||||
([`COPYING`](COPYING)) or [Unlicense][Unlicense] ([`LICENSE`](LICENSE)).
|
||||
|
||||
[CC0-1.0]: https://creativecommons.org/publicdomain/zero/1.0/legalcode
|
||||
[Unlicense]: https://unlicense.org
|
||||
|
@ -4,12 +4,12 @@ if __name__ == "__main__":
|
||||
print(":: C bytebeat generator: compiler unit")
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from os import environ, listdir, makedirs, name as os_name, \
|
||||
remove as delete_file, rmdir
|
||||
from os import getcwd, environ, makedirs, name as os_name, rename
|
||||
from os.path import exists, join as path_join
|
||||
from shlex import join as command_line_join, split as command_line_split
|
||||
from shutil import which
|
||||
from sys import stdin, stdout
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Dict, Union
|
||||
import re
|
||||
import subprocess
|
||||
@ -31,25 +31,23 @@ PATHS = {
|
||||
"include_directory": "include"
|
||||
}
|
||||
|
||||
# Add current directory before all paths for compilation
|
||||
CURRENT_DIRECTORY = getcwd()
|
||||
for key in ["src_dir", "bin_dir", "include_directory"]:
|
||||
PATHS[key] = path_join(CURRENT_DIRECTORY, PATHS[key])
|
||||
|
||||
# Resolve paths
|
||||
PATHS["template"] = path_join(PATHS["src_dir"], PATHS["template"])
|
||||
PATHS["substitute"] = path_join(PATHS["bin_dir"], PATHS["substitute"])
|
||||
PATHS["output"] = path_join(PATHS["bin_dir"], PATHS["output"])
|
||||
PATHS["substitute_kept"] = path_join(PATHS["bin_dir"], PATHS["substitute"])
|
||||
PATHS["output_kept"] = path_join(PATHS["bin_dir"], PATHS["output"])
|
||||
PATHS["fwrite_le"] = path_join(PATHS["src_dir"], PATHS["fwrite_le"])
|
||||
|
||||
# Add `.` directory before all paths for compilation
|
||||
for key in ["template", "substitute", "output", "fwrite_le",
|
||||
"include_directory"]:
|
||||
PATHS[key] = path_join(".", PATHS[key])
|
||||
|
||||
# Default parameters
|
||||
DEFAULT_PARAMETERS = {
|
||||
"CC": "cc",
|
||||
"CFLAGS": "-Ofast -march=native -mtune=native -Wall -Wextra -Wpedantic "
|
||||
"-pedantic -Wno-unused-variable -Wno-unused-but-set-variable "
|
||||
"-Wno-dangling-else -Wno-parentheses -std=c99",
|
||||
"INPUT_FILE": PATHS["substitute"],
|
||||
"OUTPUT_FILE": PATHS["output"]
|
||||
"-Wno-dangling-else -Wno-parentheses -std=c99"
|
||||
}
|
||||
|
||||
stdout_atty = hasattr(stdout, "isatty") and stdout.isatty()
|
||||
@ -63,7 +61,7 @@ def fetch(name: str):
|
||||
def read_file(path: str) -> str:
|
||||
return open(path, "r", encoding="utf-8-sig").read()
|
||||
|
||||
def rewrite_file(path: str, content: str):
|
||||
def overwrite_file(path: str, content: str) -> int:
|
||||
return open(path, "w", encoding="utf-8").write(content)
|
||||
|
||||
def read_from_file_or_stdin(path: str) -> str:
|
||||
@ -91,13 +89,27 @@ def substitute_vars(replacements: Dict[str, Union[bool, str]], text: str,
|
||||
return text
|
||||
|
||||
def run_command(*command: list[str]) -> None:
|
||||
print(command_line_join(command), flush=True)
|
||||
print("[>]", command_line_join(command), flush=True)
|
||||
if subprocess.run(command).returncode != EXIT_SUCCESS:
|
||||
raise SystemExit(EXIT_FAILURE)
|
||||
|
||||
def delete_empty_dir(path: str) -> None:
|
||||
if exists(path) and len(listdir(path)) == 0:
|
||||
rmdir(path)
|
||||
def compile_substituted_file(input_file: str, output_file: str) -> None:
|
||||
print("Compiling")
|
||||
|
||||
run_command(
|
||||
CC,
|
||||
*command_line_split(CFLAGS),
|
||||
input_file,
|
||||
PATHS["fwrite_le"],
|
||||
"-o", output_file,
|
||||
"-I" + PATHS["include_directory"]
|
||||
)
|
||||
run_command(output_file)
|
||||
|
||||
def main_workflow(input_file: str, output_file: str, \
|
||||
substitute_contents: Dict[str, str]) -> None:
|
||||
overwrite_file(input_file, substitute_contents)
|
||||
compile_substituted_file(input_file, output_file)
|
||||
|
||||
preprocessor_bool = lambda value: "1" if value else "0"
|
||||
C_str_repr = lambda s: '"' + s.replace("\\", "\\\\").replace(r'"', r'\"') + '"'
|
||||
@ -116,8 +128,6 @@ if os_name == "nt":
|
||||
]
|
||||
|
||||
CFLAGS = fetch("CFLAGS")
|
||||
INPUT_FILE = fetch("INPUT_FILE")
|
||||
OUTPUT_FILE = fetch("OUTPUT_FILE")
|
||||
if extra := fetch("CFLAGS_EXTRA"):
|
||||
CFLAGS += " " + extra
|
||||
|
||||
@ -148,8 +158,8 @@ if __name__ == "__main__":
|
||||
parser = ArgumentParser(description=\
|
||||
"Substitutes supplied C (non-JavaScript!) bytebeat into the template, "
|
||||
"then attempts to compile the instance of the template. Accepts "
|
||||
"environmental variables `CC`, `CFLAGS`, `INPUT_FILE`, `OUTPUT_FILE`. "
|
||||
"`CFLAGS_EXTRA` can be used to add to default `CFLAGS`.")
|
||||
"environmental variables `CC`, `CFLAGS`. `CFLAGS_EXTRA` can be used to "
|
||||
"add to default `CFLAGS`.")
|
||||
parser.add_argument("file", type=str,
|
||||
help="bytebeat formula file (use `-` to read from stdin)")
|
||||
parser.add_argument("-o", "--output", default="output.wav", type=str,
|
||||
@ -251,8 +261,6 @@ if __name__ == "__main__":
|
||||
args.signed = False
|
||||
|
||||
# - Compilation
|
||||
makedirs(PATHS["bin_dir"], exist_ok=True)
|
||||
|
||||
if not args.no_return: # Insert `return` statement
|
||||
# XXX: The bytebeat code is enclosed in parentheses to allow for the
|
||||
# use of commas as a comma operator, enabling more formulas to function.
|
||||
@ -337,7 +345,26 @@ if __name__ == "__main__":
|
||||
gen_length = length_formula(args.channels, samples, 0)
|
||||
loop_end = length_formula(args.channels, samples, skip_first_samples)
|
||||
|
||||
rewrite_file(PATHS["substitute"], substitute_vars({
|
||||
if is_cmd_unavailable(CC):
|
||||
print(f"Compiler {CC} is not available, searching:")
|
||||
|
||||
still_unavailable = True
|
||||
for compiler in CC_SEARCH_LIST:
|
||||
print(f"* Trying CC={compiler}", end="")
|
||||
if is_cmd_available(compiler):
|
||||
print(": OK")
|
||||
CC = compiler
|
||||
still_unavailable = False
|
||||
break
|
||||
else:
|
||||
print()
|
||||
|
||||
if still_unavailable:
|
||||
raise SystemExit("Could not find an available compiler. Please "
|
||||
"specify it by setting\nan environmental variable "
|
||||
"CC.")
|
||||
|
||||
substitute_contents = substitute_vars({
|
||||
"bytebeat_contents": bytebeat_contents,
|
||||
"output_file": C_str_repr(args.output),
|
||||
"sample_rate": actual_sample_rate,
|
||||
@ -354,6 +381,7 @@ if __name__ == "__main__":
|
||||
"loop_end_minus_1": loop_end - 1,
|
||||
"initial_time": skip_first_samples,
|
||||
"repeat_times": args.repeat,
|
||||
"length": samples,
|
||||
"wav_product": gen_length * (args.bit_depth // BITS_PER_BYTE),
|
||||
"gen_length": gen_length,
|
||||
"sequential_mode": args.mode == "sequential",
|
||||
@ -362,7 +390,7 @@ if __name__ == "__main__":
|
||||
"verbose_mode": args.verbose and not args.silent,
|
||||
"fwrite_le": PATHS["fwrite_le_header"],
|
||||
"ansi_escape_codes_supported": ansi_escape_codes_supported
|
||||
}, read_file(PATHS["template"]), args.show_substituted_values))
|
||||
}, read_file(PATHS["template"]), args.show_substituted_values)
|
||||
|
||||
if is_cmd_unavailable(CC):
|
||||
print(f"Compiler {CC} is not available, searching:")
|
||||
@ -383,20 +411,18 @@ if __name__ == "__main__":
|
||||
"specify it by setting\nan environmental variable "
|
||||
"CC.")
|
||||
|
||||
# Compile
|
||||
print("Compiling")
|
||||
if args.keep_files:
|
||||
makedirs(PATHS["bin_dir"], exist_ok=True)
|
||||
|
||||
run_command(
|
||||
CC,
|
||||
*command_line_split(CFLAGS),
|
||||
INPUT_FILE,
|
||||
PATHS["fwrite_le"],
|
||||
"-o", OUTPUT_FILE,
|
||||
"-I" + PATHS["include_directory"]
|
||||
)
|
||||
run_command(OUTPUT_FILE)
|
||||
substitute_file = PATHS["substitute_kept"]
|
||||
output_file = PATHS["output_kept"]
|
||||
|
||||
if not args.keep_files:
|
||||
delete_file(PATHS["substitute"])
|
||||
delete_file(OUTPUT_FILE)
|
||||
delete_empty_dir(PATHS["bin_dir"])
|
||||
main_workflow(substitute_file, output_file, substitute_contents)
|
||||
else:
|
||||
with TemporaryDirectory() as tmpdirname:
|
||||
temporary_path = lambda path: path_join(tmpdirname, path)
|
||||
|
||||
substitute_temp = temporary_path(PATHS["substitute"])
|
||||
output_temp = temporary_path(PATHS["output"])
|
||||
|
||||
main_workflow(substitute_temp, output_temp, substitute_contents)
|
||||
|
@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from re import sub as re_sub
|
||||
from glob import glob
|
||||
from os import remove as os_remove
|
||||
from os.path import isdir as path_isdir, isfile as path_isfile, abspath
|
||||
from shutil import rmtree
|
||||
|
||||
GITIGNORE_PATH = "./.gitignore"
|
||||
DRY_RUN = False
|
||||
|
||||
remove_comments = lambda s: re_sub(r"(?<!\\)#.*$", "", s).replace(r"\#", "#")
|
||||
|
||||
|
||||
def read_gitignore():
|
||||
res = ""
|
||||
with open(GITIGNORE_PATH, "r", encoding="utf-8-sig") as gitignore_file:
|
||||
res = gitignore_file.read().splitlines()
|
||||
return res
|
||||
|
||||
|
||||
def delete(file_path: str):
|
||||
is_dir = path_isdir(file_path)
|
||||
is_file = path_isfile(file_path)
|
||||
|
||||
if not (is_dir or is_file):
|
||||
return
|
||||
|
||||
display_file_path = abspath(file_path)
|
||||
if is_dir:
|
||||
print("Removing directory", display_file_path)
|
||||
elif is_file:
|
||||
print("Removing file", display_file_path)
|
||||
|
||||
if DRY_RUN:
|
||||
return
|
||||
|
||||
if is_dir:
|
||||
rmtree(file_path, ignore_errors=True)
|
||||
elif is_file:
|
||||
os_remove(file_path)
|
||||
|
||||
|
||||
def extend_wildcards(patterns: list):
|
||||
res = []
|
||||
for pattern in patterns:
|
||||
processed_line = remove_comments(pattern).strip()
|
||||
|
||||
if processed_line.startswith("!"):
|
||||
exception_pattern = processed_line[1:]
|
||||
exceptions = glob(exception_pattern, recursive=True)
|
||||
res = [path for path in res if path not in exceptions]
|
||||
else:
|
||||
res.extend(glob(processed_line, recursive=True))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def clean_gitignored_files():
|
||||
for file_path in extend_wildcards(read_gitignore()):
|
||||
delete(file_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
clean_gitignored_files()
|
@ -1,3 +1,5 @@
|
||||
// SPDX-License-Identifier: CC0-1.0 OR Unlicense
|
||||
|
||||
#ifndef _FWRITE_LE_H
|
||||
#define _FWRITE_LE_H
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
// SPDX-License-Identifier: CC0-1.0 OR Unlicense
|
||||
|
||||
#include "fwrite_le.h"
|
||||
|
||||
#define ORDER_NATIVE_U32 0x01234567
|
||||
@ -65,7 +67,7 @@ size_t fwrite_le(
|
||||
// case: big-endian
|
||||
size_t bytes_count = size * count;
|
||||
#if FWRITE_LE_NO_MODIFICATION
|
||||
uint8_t* bytes = malloc(bytes_count, sizeof(uint8_t));
|
||||
uint8_t* bytes = malloc(bytes_count * sizeof(uint8_t));
|
||||
if (bytes == NULL) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -1,3 +1,5 @@
|
||||
// SPDX-License-Identifier: CC0-1.0 OR Unlicense
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
@ -39,6 +41,7 @@ typedef long double bb_fp_return_t;
|
||||
#define BITS_PER_BYTE 8
|
||||
#if BIT_DEPTH <= BITS_PER_BYTE
|
||||
# define ACTUAL_BIT_DEPTH BITS_PER_BYTE
|
||||
|
||||
# define SAMPLE_TYPE uint8_t
|
||||
#elif BIT_DEPTH <= 16
|
||||
# define ACTUAL_BIT_DEPTH 16
|
||||
@ -178,18 +181,18 @@ main(void)
|
||||
"\n"
|
||||
"Sample rate: " INT2STR(SAMPLE_RATE) " Hz\n"
|
||||
"Channels: " INT2STR(CHANNELS)
|
||||
#if CHANNELS <= 2
|
||||
# if CHANNELS <= 2
|
||||
" ("
|
||||
# if CHANNELS == 2
|
||||
# if CHANNELS == 2
|
||||
"stereo"
|
||||
# else
|
||||
# else
|
||||
"mono"
|
||||
# endif
|
||||
# endif
|
||||
")"
|
||||
#endif
|
||||
# endif
|
||||
"\n"
|
||||
"Bit depth: "
|
||||
#if !IS_SIGNED
|
||||
# if !IS_SIGNED
|
||||
"un"
|
||||
#endif
|
||||
"signed " INT2STR(BIT_DEPTH) "-bit"
|
||||
@ -374,14 +377,14 @@ main(void)
|
||||
"%sremaining samples = %18" PRIuMAX " (%6.2Lf%% done)"
|
||||
#if SEQUENTIAL_MODE
|
||||
" (part %" PRIuMAX "/%" PRIuMAX ")"
|
||||
#endif
|
||||
# endif
|
||||
,
|
||||
_ANSI_CLEAR_STRING,
|
||||
LOOP_END_MINUS_1 - time,
|
||||
((long double) time * 100) / (long double) LOOP_END_MINUS_1
|
||||
#if SEQUENTIAL_MODE
|
||||
, (uintmax_t) seq + 1, (uintmax_t) MAX
|
||||
#endif
|
||||
# endif
|
||||
);
|
||||
smart_fflush();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user