initial commit

This commit is contained in:
Intel A80486DX2-66 2023-11-16 22:01:41 +03:00
commit ae04b22c7a
Signed by: 80486DX2-66
GPG Key ID: 83631EF27054609B
10 changed files with 487 additions and 0 deletions

12
.clang-format Normal file
View File

@ -0,0 +1,12 @@
BasedOnStyle: Mozilla
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignTrailingComments: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BreakBeforeBinaryOperators: None
ColumnLimit: 80
IndentWidth: 4
SpaceBeforeRangeBasedForLoopColon: false
TabWidth: 4
UseTab: Always

66
.gitignore vendored Normal file
View File

@ -0,0 +1,66 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> products
# Substituted code
formula_substituted.*
# Executables
*.a
*.out
*.exe
render_bytebeat.*
# Output
output.wav

15
EXAMPLE_USAGE.txt Normal file
View File

@ -0,0 +1,15 @@
$ cat | ./bytebeat_compiler.py - && ./render_bytebeat
t&(t>>7)-t&t>>8
Compiling
:: C bytebeat generator runtime unit
Sample rate: 44100 Hz
Channels: 1 (mono)
Bit depth: unsigned 8-bit
Duration: 30 seconds
remaining samples = 0 (100.00% done)
Writing out file output.wav...
Done!
$

10
LICENSE Normal file
View File

@ -0,0 +1,10 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# C_bytebeat_render
a bytebeat rendering engine in C (no support of JavaScript bytebeat)

99
bytebeat_compiler.py Normal file
View File

@ -0,0 +1,99 @@
#!/usr/bin/python3
DEFAULT_PARAMETERS = {
"CC": "gcc",
"CC_FLAGS": "-Os -Wall -Werror -Wpedantic",
"INPUT_FILES": ["engine.c", "formula_substituted.c"],
"OUTPUT_FILE": "render_bytebeat"
}
from os.path import exists
from os import system, environ
from argparse import ArgumentParser
from sys import stdin
import subprocess
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
def substitute_value(placeholder: str, replacement, text: str) -> str:
return text.replace(f"`{placeholder}`", str(replacement))
CC = fetch("CC")
CC_FLAGS = fetch("CC_FLAGS")
INPUT_FILES = fetch("INPUT_FILES")
OUTPUT_FILE = fetch("OUTPUT_FILE")
if __name__ == "__main__":
print("Bytebeat compiler")
parser = ArgumentParser(description=\
"Substitutes supplied C (non-JavaScript!) bytebeat into the template, "
"then attempts to compile the instance of the template. Uses "
"environmental variables `CC`, `CC_FLAGS`, `INPUT_FILES`, "
"`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)")
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
# Substitute all placeholders in formula_template.c -> formula_substitute.c
if not args.no_return: # Insert return statement
bytebeat_contents = f"\treturn\n\n{bytebeat_contents}"
substitute_c = read_file("formula_template.c")
substitute_c = substitute_value("bytebeat_contents",
bytebeat_contents, substitute_c)
rewrite_file("formula_substituted.c", substitute_c)
# Substitute all placeholders in formula_template.h -> formula_substitute.h
substitute_h = read_file("formula_template.h")
substitute_h = substitute_value("sample_rate",
args.sample_rate, substitute_h)
substitute_h = substitute_value("bit_depth",
args.bit_depth, substitute_h)
substitute_h = substitute_value("is_signed",
"1" if args.signed else "0", substitute_h)
substitute_h = substitute_value("channels",
args.channels, substitute_h)
substitute_h = substitute_value("seconds",
args.seconds, substitute_h)
rewrite_file("formula_substituted.h", substitute_h)
# Compile by invoking the shell script
print("Compiling")
# Let system execute aliases by calling os.system
system(" ".join([CC, CC_FLAGS, *INPUT_FILES, "-o", OUTPUT_FILE]))

181
engine.c Normal file
View File

@ -0,0 +1,181 @@
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "formula_substituted.h"
#if defined(_WIN32)
#define __ANSI_CLEAR_STRING "\r"
#elif defined(__unix__) || defined(__linux__)
#define __ANSI_CLEAR_STRING "\x1B[2K\r"
#else
#define __ANSI_CLEAR_STRING "\n"
#endif
const char* ANSI_CLEAR = __ANSI_CLEAR_STRING;
#define PRODUCT (SAMPLE_RATE * SECONDS * CHANNELS)
#define FREQUENCY_OF_STATUS_REPORTING 5000
#define BIT_DEPTH_LIMITER ((1 << BIT_DEPTH) - 1)
#define PCM_COEFFICIENT ((1 << (BIT_DEPTH - 1)) - 1)
#define unsigned_to_signed(x) (x - PCM_COEFFICIENT)
#define signed_to_unsigned(x) (x + PCM_COEFFICIENT)
unsigned int dbgpnt_counter = 0;
const char* dbgpnt_labels[3] = { "memory allocation",
"bytebeat generation",
"writing file" };
#define dbgpnt_labels_size \
(unsigned int)(sizeof(dbgpnt_labels) / sizeof(dbgpnt_labels[0]))
bool silent_mode = 0;
bool debug_mode = 0;
void
debug_print(void)
{
if (!debug_mode)
return;
int has_label = dbgpnt_counter <= dbgpnt_labels_size;
printf("[OK] ");
if (has_label)
printf("%s", dbgpnt_labels[dbgpnt_counter++]);
else
printf("debug point %d", ++dbgpnt_counter);
return;
}
long double
random(void)
{
static int initialized = 0;
if (!initialized) {
srand(time(NULL));
initialized = 1;
}
return (long double)rand() / RAND_MAX;
}
int
main(int argc, char** argv)
{
silent_mode = argc > 1 && argv[1] != NULL &&
(!strcmp(argv[1], "-s") || !strcmp(argv[1], "--silent"));
debug_mode = argc > 1 && argv[1] != NULL &&
(!strcmp(argv[1], "-d") || !strcmp(argv[1], "--debug"));
printf(":: C bytebeat generator runtime unit\n");
fflush(stdout);
if (!silent_mode) {
printf(
"\n"
"Sample rate: %d Hz\n"
"Channels: %d%s\n"
"Bit depth: %ssigned %d-bit\n"
"Duration: ",
SAMPLE_RATE,
CHANNELS,
CHANNELS == 1 ? " (mono)" : (CHANNELS > 2 ? "" : " (stereo)"),
IS_SIGNED ? "" : "un",
BIT_DEPTH);
if (SECONDS >= 3600)
printf(
"%d:%02d:%02d",
SECONDS / 3600,
(SECONDS / 60) % 60,
SECONDS % 60);
else if (SECONDS >= 60)
printf("%d:%02d", SECONDS / 60, SECONDS % 60);
else
printf("%d seconds", SECONDS);
printf("\n\n");
fflush(stdout);
}
SAMPLE_TYPE* q = calloc(PRODUCT, sizeof(SAMPLE_TYPE));
if (q == NULL)
return 1;
debug_print();
for (uintmax_t w = 0; w < PRODUCT; w++) {
long double res = bytebeat((long double)w);
if (IS_SIGNED)
q[w] = (SAMPLE_TYPE)signed_to_unsigned(res) & BIT_DEPTH_LIMITER;
else
q[w] = (SAMPLE_TYPE)res & BIT_DEPTH_LIMITER;
#if BIT_DEPTH < 8
q[(size_t)w] = (SAMPLE_TYPE)((long double)q[(size_t)w] *
((long double)BIT_DEPTH / 8.0));
#endif
if (
!silent_mode &&
(w % FREQUENCY_OF_STATUS_REPORTING == 0 || w >= PRODUCT - 1)) {
printf(
"%sremaining samples = %18" PRIuMAX " (%.2Lf%% done)",
ANSI_CLEAR,
PRODUCT - w - 1,
(long double)w * 100 / (long double)PRODUCT);
fflush(stdout);
}
}
printf("%s", ANSI_CLEAR);
debug_print();
printf("\nWriting out file output.wav...\n");
fflush(stdout);
FILE* r = fopen("output.wav", "wb");
if (r == NULL || !r)
return 1;
fwrite("RIFF", 1, 4, r);
uint32_t t = PRODUCT;
fwrite(&t, sizeof(t), 1, r);
fwrite("WAVE", 1, 4, r);
fwrite("fmt ", 1, 4, r);
uint32_t y = 16; // what? 16-bit depth? or [something else]?
fwrite(&y, sizeof(y), 1, r);
uint16_t u = 1; // 1 what? 1 = unsigned?
fwrite(&u, sizeof(u), 1, r);
uint16_t i = CHANNELS;
fwrite(&i, sizeof(i), 1, r);
uint32_t o = SAMPLE_RATE;
fwrite(&o, sizeof(o), 1, r);
uint32_t s = SAMPLE_RATE * i;
fwrite(&s, sizeof(s), 1, r);
uint16_t p = i;
fwrite(&p, sizeof(p), 1, r);
uint16_t a = BIT_DEPTH >= 8 ? BIT_DEPTH : 8;
fwrite(&a, sizeof(a), 1, r);
fwrite("data", 1, 4, r);
fwrite(&t, sizeof(t), 1, r);
fwrite(q, sizeof(uint8_t), PRODUCT, r);
fclose(r);
debug_print();
free(q);
printf("Done!\n");
return 0;
}

13
formula_template.c Normal file
View File

@ -0,0 +1,13 @@
#include <stdint.h>
#include "formula_substituted.h"
SAMPLE_TYPE
bytebeat(long double w)
{
uintmax_t t = (uintmax_t)w;
`bytebeat_contents`
;
}

23
formula_template.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef _FORMULA_TEMPLATE_H
#define _FORMULA_TEMPLATE_H
#define SAMPLE_RATE `sample_rate`
#define BIT_DEPTH `bit_depth`
#define IS_SIGNED `is_signed`
#define CHANNELS `channels`
#define SECONDS `seconds`
#if BIT_DEPTH <= 8
#define SAMPLE_TYPE uint8_t
#elif BIT_DEPTH >= 16
#if IS_SIGNED
#define SAMPLE_TYPE int16_t
#else
#define SAMPLE_TYPE uint16_t
#endif
#endif
SAMPLE_TYPE
bytebeat(long double w);
#endif /* _FORMULA_TEMPLATE_H */

65
gitignore_clean.py Normal file
View File

@ -0,0 +1,65 @@
#!/usr/bin/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()