merge from WIP
This commit is contained in:
commit
c3b470f842
@ -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!
|
||||||
```
|
```
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
#ifndef _FWRITE_LE_H
|
||||||
#define _FWRITE_LE_H
|
#define _FWRITE_LE_H
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user