1
0

merge from WIP

This commit is contained in:
Intel A80486DX2-66 2024-08-27 00:32:29 +03:00
commit c3b470f842
Signed by: 80486DX2-66
GPG Key ID: 83631EF27054609B
7 changed files with 93 additions and 122 deletions

View File

@ -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!
```

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -1,3 +1,5 @@
// SPDX-License-Identifier: CC0-1.0 OR Unlicense
#ifndef _FWRITE_LE_H
#define _FWRITE_LE_H

View File

@ -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);

View File

@ -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();
}