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 ## Example usage
```text ```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 :: C bytebeat generator: compiler unit
Reading from STDIN...
Compiling 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 :: C bytebeat generator runtime unit
Sample rate: 44100 Hz Sample rate: 44100 Hz
@ -11,9 +14,7 @@ Channels: 1 (mono)
Bit depth: unsigned 8-bit Bit depth: unsigned 8-bit
Duration: 30 seconds Duration: 30 seconds
remaining samples = 0 (100.00% done) Writing WAVE headers...
Writing out file output.wav...
Done!
$ Done!
``` ```

View File

@ -7,6 +7,8 @@
## License ## License
Dual-licensed under the [Unlicense](http://unlicense.org) Dual-licensed under the [Creative Commons Zero 1.0 Universal][CC0-1.0]
([`LICENSE`](LICENSE)) and Creative Commons Zero 1.0 Universal ([`COPYING`](COPYING)) or [Unlicense][Unlicense] ([`LICENSE`](LICENSE)).
([`COPYING`](COPYING)).
[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") print(":: C bytebeat generator: compiler unit")
from argparse import ArgumentParser from argparse import ArgumentParser
from os import environ, listdir, makedirs, name as os_name, \ from os import getcwd, environ, makedirs, name as os_name, rename
remove as delete_file, rmdir
from os.path import exists, join as path_join from os.path import exists, join as path_join
from shlex import join as command_line_join, split as command_line_split from shlex import join as command_line_join, split as command_line_split
from shutil import which from shutil import which
from sys import stdin, stdout from sys import stdin, stdout
from tempfile import TemporaryDirectory
from typing import Dict, Union from typing import Dict, Union
import re import re
import subprocess import subprocess
@ -31,25 +31,23 @@ PATHS = {
"include_directory": "include" "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 # Resolve paths
PATHS["template"] = path_join(PATHS["src_dir"], PATHS["template"]) PATHS["template"] = path_join(PATHS["src_dir"], PATHS["template"])
PATHS["substitute"] = path_join(PATHS["bin_dir"], PATHS["substitute"]) PATHS["substitute_kept"] = path_join(PATHS["bin_dir"], PATHS["substitute"])
PATHS["output"] = path_join(PATHS["bin_dir"], PATHS["output"]) PATHS["output_kept"] = path_join(PATHS["bin_dir"], PATHS["output"])
PATHS["fwrite_le"] = path_join(PATHS["src_dir"], PATHS["fwrite_le"]) 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
DEFAULT_PARAMETERS = { DEFAULT_PARAMETERS = {
"CC": "cc", "CC": "cc",
"CFLAGS": "-Ofast -march=native -mtune=native -Wall -Wextra -Wpedantic " "CFLAGS": "-Ofast -march=native -mtune=native -Wall -Wextra -Wpedantic "
"-pedantic -Wno-unused-variable -Wno-unused-but-set-variable " "-pedantic -Wno-unused-variable -Wno-unused-but-set-variable "
"-Wno-dangling-else -Wno-parentheses -std=c99", "-Wno-dangling-else -Wno-parentheses -std=c99"
"INPUT_FILE": PATHS["substitute"],
"OUTPUT_FILE": PATHS["output"]
} }
stdout_atty = hasattr(stdout, "isatty") and stdout.isatty() stdout_atty = hasattr(stdout, "isatty") and stdout.isatty()
@ -63,7 +61,7 @@ def fetch(name: str):
def read_file(path: str) -> str: def read_file(path: str) -> str:
return open(path, "r", encoding="utf-8-sig").read() 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) return open(path, "w", encoding="utf-8").write(content)
def read_from_file_or_stdin(path: str) -> str: 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 return text
def run_command(*command: list[str]) -> None: 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: if subprocess.run(command).returncode != EXIT_SUCCESS:
raise SystemExit(EXIT_FAILURE) raise SystemExit(EXIT_FAILURE)
def delete_empty_dir(path: str) -> None: def compile_substituted_file(input_file: str, output_file: str) -> None:
if exists(path) and len(listdir(path)) == 0: print("Compiling")
rmdir(path)
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" preprocessor_bool = lambda value: "1" if value else "0"
C_str_repr = lambda s: '"' + s.replace("\\", "\\\\").replace(r'"', r'\"') + '"' C_str_repr = lambda s: '"' + s.replace("\\", "\\\\").replace(r'"', r'\"') + '"'
@ -116,8 +128,6 @@ if os_name == "nt":
] ]
CFLAGS = fetch("CFLAGS") CFLAGS = fetch("CFLAGS")
INPUT_FILE = fetch("INPUT_FILE")
OUTPUT_FILE = fetch("OUTPUT_FILE")
if extra := fetch("CFLAGS_EXTRA"): if extra := fetch("CFLAGS_EXTRA"):
CFLAGS += " " + extra CFLAGS += " " + extra
@ -148,8 +158,8 @@ if __name__ == "__main__":
parser = ArgumentParser(description=\ parser = ArgumentParser(description=\
"Substitutes supplied C (non-JavaScript!) bytebeat into the template, " "Substitutes supplied C (non-JavaScript!) bytebeat into the template, "
"then attempts to compile the instance of the template. Accepts " "then attempts to compile the instance of the template. Accepts "
"environmental variables `CC`, `CFLAGS`, `INPUT_FILE`, `OUTPUT_FILE`. " "environmental variables `CC`, `CFLAGS`. `CFLAGS_EXTRA` can be used to "
"`CFLAGS_EXTRA` can be used to add to default `CFLAGS`.") "add to default `CFLAGS`.")
parser.add_argument("file", type=str, parser.add_argument("file", type=str,
help="bytebeat formula file (use `-` to read from stdin)") help="bytebeat formula file (use `-` to read from stdin)")
parser.add_argument("-o", "--output", default="output.wav", type=str, parser.add_argument("-o", "--output", default="output.wav", type=str,
@ -251,8 +261,6 @@ if __name__ == "__main__":
args.signed = False args.signed = False
# - Compilation # - Compilation
makedirs(PATHS["bin_dir"], exist_ok=True)
if not args.no_return: # Insert `return` statement if not args.no_return: # Insert `return` statement
# XXX: The bytebeat code is enclosed in parentheses to allow for the # XXX: The bytebeat code is enclosed in parentheses to allow for the
# use of commas as a comma operator, enabling more formulas to function. # 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) gen_length = length_formula(args.channels, samples, 0)
loop_end = length_formula(args.channels, samples, skip_first_samples) 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, "bytebeat_contents": bytebeat_contents,
"output_file": C_str_repr(args.output), "output_file": C_str_repr(args.output),
"sample_rate": actual_sample_rate, "sample_rate": actual_sample_rate,
@ -354,6 +381,7 @@ if __name__ == "__main__":
"loop_end_minus_1": loop_end - 1, "loop_end_minus_1": loop_end - 1,
"initial_time": skip_first_samples, "initial_time": skip_first_samples,
"repeat_times": args.repeat, "repeat_times": args.repeat,
"length": samples,
"wav_product": gen_length * (args.bit_depth // BITS_PER_BYTE), "wav_product": gen_length * (args.bit_depth // BITS_PER_BYTE),
"gen_length": gen_length, "gen_length": gen_length,
"sequential_mode": args.mode == "sequential", "sequential_mode": args.mode == "sequential",
@ -362,7 +390,7 @@ if __name__ == "__main__":
"verbose_mode": args.verbose and not args.silent, "verbose_mode": args.verbose and not args.silent,
"fwrite_le": PATHS["fwrite_le_header"], "fwrite_le": PATHS["fwrite_le_header"],
"ansi_escape_codes_supported": ansi_escape_codes_supported "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): if is_cmd_unavailable(CC):
print(f"Compiler {CC} is not available, searching:") print(f"Compiler {CC} is not available, searching:")
@ -383,20 +411,18 @@ if __name__ == "__main__":
"specify it by setting\nan environmental variable " "specify it by setting\nan environmental variable "
"CC.") "CC.")
# Compile if args.keep_files:
print("Compiling") makedirs(PATHS["bin_dir"], exist_ok=True)
run_command( substitute_file = PATHS["substitute_kept"]
CC, output_file = PATHS["output_kept"]
*command_line_split(CFLAGS),
INPUT_FILE,
PATHS["fwrite_le"],
"-o", OUTPUT_FILE,
"-I" + PATHS["include_directory"]
)
run_command(OUTPUT_FILE)
if not args.keep_files: main_workflow(substitute_file, output_file, substitute_contents)
delete_file(PATHS["substitute"]) else:
delete_file(OUTPUT_FILE) with TemporaryDirectory() as tmpdirname:
delete_empty_dir(PATHS["bin_dir"]) 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 #ifndef _FWRITE_LE_H
#define _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" #include "fwrite_le.h"
#define ORDER_NATIVE_U32 0x01234567 #define ORDER_NATIVE_U32 0x01234567
@ -65,7 +67,7 @@ size_t fwrite_le(
// case: big-endian // case: big-endian
size_t bytes_count = size * count; size_t bytes_count = size * count;
#if FWRITE_LE_NO_MODIFICATION #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) { if (bytes == NULL) {
perror("malloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -1,3 +1,5 @@
// SPDX-License-Identifier: CC0-1.0 OR Unlicense
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
@ -39,6 +41,7 @@ typedef long double bb_fp_return_t;
#define BITS_PER_BYTE 8 #define BITS_PER_BYTE 8
#if BIT_DEPTH <= BITS_PER_BYTE #if BIT_DEPTH <= BITS_PER_BYTE
# define ACTUAL_BIT_DEPTH BITS_PER_BYTE # define ACTUAL_BIT_DEPTH BITS_PER_BYTE
# define SAMPLE_TYPE uint8_t # define SAMPLE_TYPE uint8_t
#elif BIT_DEPTH <= 16 #elif BIT_DEPTH <= 16
# define ACTUAL_BIT_DEPTH 16 # define ACTUAL_BIT_DEPTH 16
@ -178,18 +181,18 @@ main(void)
"\n" "\n"
"Sample rate: " INT2STR(SAMPLE_RATE) " Hz\n" "Sample rate: " INT2STR(SAMPLE_RATE) " Hz\n"
"Channels: " INT2STR(CHANNELS) "Channels: " INT2STR(CHANNELS)
#if CHANNELS <= 2 # if CHANNELS <= 2
" (" " ("
# if CHANNELS == 2 # if CHANNELS == 2
"stereo" "stereo"
# else # else
"mono" "mono"
# endif # endif
")" ")"
#endif # endif
"\n" "\n"
"Bit depth: " "Bit depth: "
#if !IS_SIGNED # if !IS_SIGNED
"un" "un"
#endif #endif
"signed " INT2STR(BIT_DEPTH) "-bit" "signed " INT2STR(BIT_DEPTH) "-bit"
@ -374,14 +377,14 @@ main(void)
"%sremaining samples = %18" PRIuMAX " (%6.2Lf%% done)" "%sremaining samples = %18" PRIuMAX " (%6.2Lf%% done)"
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
" (part %" PRIuMAX "/%" PRIuMAX ")" " (part %" PRIuMAX "/%" PRIuMAX ")"
#endif # endif
, ,
_ANSI_CLEAR_STRING, _ANSI_CLEAR_STRING,
LOOP_END_MINUS_1 - time, LOOP_END_MINUS_1 - time,
((long double) time * 100) / (long double) LOOP_END_MINUS_1 ((long double) time * 100) / (long double) LOOP_END_MINUS_1
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
, (uintmax_t) seq + 1, (uintmax_t) MAX , (uintmax_t) seq + 1, (uintmax_t) MAX
#endif # endif
); );
smart_fflush(); smart_fflush();
} }