1
0

Merge branch 'WIP'

This commit is contained in:
パチュリー・ノーレッジ 2024-11-17 17:54:59 +03:00
commit 2f0975287f
Signed by: 80486DX2-66
GPG Key ID: 83631EF27054609B
6 changed files with 381 additions and 104 deletions

View File

@ -13,6 +13,7 @@ from shutil import which
from sys import stdin, stdout from sys import stdin, stdout
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from typing import Dict, Union from typing import Dict, Union
import re
import subprocess import subprocess
# Definitions # Definitions
@ -53,6 +54,18 @@ DEFAULT_PARAMETERS = {
stdout_atty = hasattr(stdout, "isatty") and stdout.isatty() stdout_atty = hasattr(stdout, "isatty") and stdout.isatty()
def is_decimal_number(s: str) -> bool:
if s.count('.') > 1: # More than one decimal point
return False
if s.startswith(('+', '-')):
s = s[1:] # Remove the sign for further checks
if s.replace('.', '', 1).isdigit():
return True
return False
def fetch(name: str): def fetch(name: str):
if from_env := environ.get(name): if from_env := environ.get(name):
return from_env return from_env
@ -97,7 +110,7 @@ def run_command(*command: list[str], stage: str) -> None:
raise SystemExit(EXIT_FAILURE) raise SystemExit(EXIT_FAILURE)
def compile_substituted_file(input_file: str, executable_file: str) -> None: def compile_substituted_file(input_file: str, output_file: str) -> None:
print("Compiling") print("Compiling")
run_command( run_command(
@ -105,16 +118,16 @@ def compile_substituted_file(input_file: str, executable_file: str) -> None:
*command_line_split(CFLAGS), *command_line_split(CFLAGS),
input_file, input_file,
PATHS["fwrite_le"], PATHS["fwrite_le"],
"-o", executable_file, "-o", output_file,
"-I" + PATHS["include_directory"], "-I" + PATHS["include_directory"],
stage="compilation" stage="compilation"
) )
run_command(executable_file, stage="rendering") run_command(output_file, stage="rendering")
def main_workflow(input_file: str, executable_file: str, \ def main_workflow(input_file: str, output_file: str, \
substitute_contents: Dict[str, str]) -> None: substitute_contents: Dict[str, str]) -> None:
overwrite_file(input_file, substitute_contents) overwrite_file(input_file, substitute_contents)
compile_substituted_file(input_file, executable_file) 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'\"') + '"'
@ -136,6 +149,26 @@ CFLAGS = fetch("CFLAGS")
if extra := fetch("CFLAGS_EXTRA"): if extra := fetch("CFLAGS_EXTRA"):
CFLAGS += " " + extra CFLAGS += " " + extra
parameter_line_regex = re.compile(r"^\/\/ *RENDER PARAMETERS *: ", re.MULTILINE)
ALLOWED_ARGUMENTS_FROM_FILE = (
"sample_rate",
"final_sample_rate",
"bit_depth",
"signed",
"channels",
"custom_return_code",
)
DEFAULT_ARGS = {
"sample_rate": 8000,
"final_sample_rate": 0,
"bit_depth": 8,
"signed": False,
"channels": 1,
"custom_return_code": False
}
is_cmd_available = lambda cmd: which(cmd) is not None is_cmd_available = lambda cmd: which(cmd) is not None
is_cmd_unavailable = lambda cmd: which(cmd) is None is_cmd_unavailable = lambda cmd: which(cmd) is None
@ -148,38 +181,46 @@ if __name__ == "__main__":
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,
help="specify output WAVE file path (default is `output.wav`") help="specify output WAVE file path : default is `output.wav`")
parser.add_argument("-r", "--sample-rate", default=8000, type=int, parser.add_argument("-r", "--sample-rate", default=None, type=int,
help="sample rate (Hz)") help="sample rate (Hz)")
parser.add_argument("-p", "--final-sample-rate", default=None, type=int, parser.add_argument("-p", "--final-sample-rate", default=None, type=int,
help="convert the output to a different sample rate (usually one that " help="convert the output to a different sample rate (usually one that "
"is set in the system, to improve sound quality) during generation " "is set in the system, to improve sound quality) during generation "
"(not just reinterpretation)") "(not just reinterpretation)")
parser.add_argument("-b", "--bit-depth", default=8, type=int, parser.add_argument("-b", "--bit-depth", default=None, type=int,
help="bit depth") help="bit depth")
parser.add_argument("-s", "--signed", default=False, action="store_true", parser.add_argument("-s", "--signed", default=None, action="store_true",
help="is signed?") help="is signed?")
parser.add_argument("-u", "--unsigned", default=None, action="store_true",
help="is unsigned? (overrides the 'is signed' parameter)")
parser.add_argument("-R", "--precalculate-ratio", default=False, parser.add_argument("-R", "--precalculate-ratio", default=False,
action="store_true", action="store_true",
help="precalculate sample ratio to speed up rendering (may produce " help="precalculate sample ratio to speed up rendering (may produce "
"inaccurate results") "inaccurate results)")
parser.add_argument("-m", "--faster-sample-ratio-math", default=False, parser.add_argument("-m", "--faster-sample-ratio-math", default=False,
action="store_true", action="store_true",
help="faster sample ratio math (implies argument -R)") help="faster sample ratio math (implies argument -R)")
parser.add_argument("-f", "--floating-point", default=False, parser.add_argument("-f", "--floating-point", default=False,
action="store_true", help="use floating point as the return type") action="store_true", help="use floating point as the return type")
parser.add_argument("-c", "--channels", default=1, type=int, parser.add_argument("-c", "--channels", default=None, type=int,
help="amount of channels") help="amount of channels")
parser.add_argument("-t", "--seconds", default=None, type=Decimal, parser.add_argument("-t", "--seconds", default=None, type=Decimal,
help="length in seconds (samples = sample rate * seconds) : " help="length in seconds (samples = sample rate * seconds) : "
"default = 30 seconds") "default = 30 seconds")
parser.add_argument("-l", "--samples", default=None, type=int, parser.add_argument("-l", "--samples", default=None, type=int,
help="length in samples (adds to `-t`; supports negative numbers) : " help="length in samples (adds to `-t`; supports negative numbers) : "
"default = +0 samples") "default = seconds + 0 samples")
parser.add_argument("-a", "--custom-return-code", default=False, parser.add_argument("-S", "--skip-first", default=None, type=str,
action="store_true", help="skip first `A` seconds and `B` samples: in format `As`, `B` or "
help="do not insert return statement before the code") "`AsB` : default = 0")
parser.add_argument("-u", "--mode", default="sequential", type=str, parser.add_argument("-k", "--repeat", default=0, type=int,
help="how many times to repeat the bytebeat : "
"default = 0")
parser.add_argument("-a", "--custom-return-code", default=None,
action="store_true",
help="do not insert return statement before the code")
parser.add_argument("-U", "--mode", default="sequential", type=str,
help="mode of writing: `sequential` or `instant` (the latter is not " help="mode of writing: `sequential` or `instant` (the latter is not "
"recommended, since the whole result would be stored in RAM)") "recommended, since the whole result would be stored in RAM)")
parser.add_argument("-n", "--block-size", default=65536, type=int, parser.add_argument("-n", "--block-size", default=65536, type=int,
@ -203,6 +244,42 @@ if __name__ == "__main__":
if not bytebeat_contents: if not bytebeat_contents:
raise SystemExit("Empty file or STDIN") raise SystemExit("Empty file or STDIN")
# - Parse arguments from file
used_parameter_line = False
for line in bytebeat_contents.splitlines():
if (match := re.search(parameter_line_regex, line)) and \
not used_parameter_line:
used_parameter_line = True
parsed_parameters = line[match.start(0):].split(",")
for parameter in parsed_parameters:
kv = [x.strip() for x in parameter.split("=")]
kv[0] = kv[0].split(" ")[-1]
key = None
value = None
if len(kv) == 1:
key, value = kv[0], True
elif len(kv) == 2:
key, value = kv[0], int(kv[1])
else:
break
# Apply the argument only if it was not used by user yet and is
# allowed to be set
if (key not in args or getattr(args, key) is None) and \
key in ALLOWED_ARGUMENTS_FROM_FILE:
setattr(args, key, value)
# - Set default values
for key, value in DEFAULT_ARGS.items():
if getattr(args, key) is None:
setattr(args, key, value)
if args.unsigned is True:
args.signed = False
# - Compilation # - Compilation
if not args.custom_return_code: # Insert `return` statement if not args.custom_return_code: # 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
@ -213,7 +290,7 @@ if __name__ == "__main__":
final_sample_rate_code = "" final_sample_rate_code = ""
if args.faster_sample_ratio_math: if args.faster_sample_ratio_math:
args.precalculate_ratio = True args.precalculate_ratio = True
if args.precalculate_ratio and not args.final_sample_rate is None: if args.precalculate_ratio and args.final_sample_rate != 0:
if args.faster_sample_ratio_math: if args.faster_sample_ratio_math:
sample_rate_ratio = args.sample_rate / args.final_sample_rate sample_rate_ratio = args.sample_rate / args.final_sample_rate
final_sample_rate_code = f"time *= {sample_rate_ratio}L;" final_sample_rate_code = f"time *= {sample_rate_ratio}L;"
@ -223,7 +300,8 @@ if __name__ == "__main__":
args.sample_rate = args.final_sample_rate args.sample_rate = args.final_sample_rate
final_sample_rate = \ final_sample_rate = \
value if (value := args.final_sample_rate) else original_sample_rate value if (value := args.final_sample_rate) != 0 \
else original_sample_rate
samples = 0 samples = 0
while True: while True:
no_seconds = args.seconds is None or args.seconds == 0 no_seconds = args.seconds is None or args.seconds == 0
@ -261,6 +339,43 @@ if __name__ == "__main__":
ansi_escape_codes_supported = args.color == "auto" and stdout_atty or \ ansi_escape_codes_supported = args.color == "auto" and stdout_atty or \
args.color == "always" args.color == "always"
actual_sample_rate = \
value if (value := args.final_sample_rate) else args.sample_rate
# - Parse the '--skip-first' argument
if args.skip_first is None:
skip_first_samples = 0
else:
encountered_point = False
encountered_s = False
for character in args.skip_first:
if character.isdigit() or character == "." and \
not encountered_point or character == "s" and not encountered_s:
if character == ".":
encountered_point = True
elif character == "s":
encountered_s = True
else:
raise SystemExit( "Invalid --skip-first format: "
f"`{args.skip_first}`")
skip_first = \
[Decimal(x) if is_decimal_number(x) else 0 for x in \
args.skip_first.split("s")]
skip_first_samples = 0
if len(skip_first) == 1:
skip_first += [0]
skip_first_samples = skip_first[0] * actual_sample_rate + skip_first[1]
# round the number of skipped first samples
skip_first_samples = ceil(skip_first_samples)
length_formula = lambda channels, samples, n: channels * (samples + n)
gen_length = length_formula(args.channels, samples, 0)
loop_end = length_formula(args.channels, samples, skip_first_samples)
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:")
@ -283,8 +398,7 @@ if __name__ == "__main__":
substitute_contents = substitute_vars({ 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": \ "sample_rate": actual_sample_rate,
value if (value := args.final_sample_rate) else args.sample_rate,
"original_sample_rate": original_sample_rate, "original_sample_rate": original_sample_rate,
"final_sample_rate_code": final_sample_rate_code, "final_sample_rate_code": final_sample_rate_code,
"bit_depth": args.bit_depth, "bit_depth": args.bit_depth,
@ -293,7 +407,11 @@ if __name__ == "__main__":
"faster_sample_ratio_math": args.precalculate_ratio, "faster_sample_ratio_math": args.precalculate_ratio,
"fp_return_type": args.floating_point, "fp_return_type": args.floating_point,
"channels": args.channels, "channels": args.channels,
"length": samples, "running_length": samples,
"loop_end": loop_end,
"loop_end_minus_1": loop_end - 1,
"initial_time": skip_first_samples,
"repeat_times": args.repeat,
"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",

5
samples/beeper.c Normal file
View File

@ -0,0 +1,5 @@
// RENDER PARAMETERS: sample_rate = 44100, custom_return_code
bb_counter_t u = t << 1;
SAMPLE_TYPE x = u & u >> 8;
return (x | 127) + 63;

3
samples/cykstep.c Normal file
View File

@ -0,0 +1,3 @@
// RENDER PARAMETERS: sample_rate = 44100, final_sample_rate = 48000, signed
t/=3,(t*(t+(444+(t/444)))&((t>>9)>4095 ? 4095 : (t>>(9+(3&t>>13)))))>>(3&t>>(5+(3&t>>13)))

7
samples/melody.c Normal file
View File

@ -0,0 +1,7 @@
// RENDER PARAMETERS: sample_rate = 44100, custom_return_code
// FIXME: The sound disappears after a few cycles
static const long double array[] = {1, 1.25, 1.5, 2};
long double v = array[3 & (t >> 13)];
return (long double) t * v / 3.1415926535;

6
samples/pwm-sierpinski.c Normal file
View File

@ -0,0 +1,6 @@
// "pwm serpinski harmony" by SArpnt
// URL: https://www.reddit.com/r/bytebeat/comments/g9106h/pwm_serpinski_harmony/
// RENDER PARAMETERS: sample_rate = 8000, final_sample_rate = 44100
t&(t>>7)-t&t>>8

View File

@ -11,6 +11,42 @@
#include "`fwrite_le`" #include "`fwrite_le`"
#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L
# define FSEEK_MACRO fseeko
# define FSEEK_FUNCTION_NAME "fseeko"
typedef off_t file_offset_t;
#else
# define FSEEK_MACRO fseek
# define FSEEK_FUNCTION_NAME "fseek"
typedef int file_offset_t;
#endif
#define STRINGIZE(x) #x
#define FILE_IO_NO_FAIL(function, ptr, size, nitems, stream) do { \
if (function(ptr, size, nitems, stream) != nitems) { \
/* clean up */ \
free(buffer); \
\
perror(STRINGIZE(function)); \
exit(EXIT_FAILURE); \
} \
} while (0)
#define FSEEK_NO_FAIL(...) do { \
if (FSEEK_MACRO(__VA_ARGS__) == -1) { \
perror(FSEEK_FUNCTION_NAME); \
exit(EXIT_FAILURE); \
} \
} while (0)
#define FREAD_NO_FAIL(...) FILE_IO_NO_FAIL(fread, __VA_ARGS__)
#define FWRITE_NO_FAIL(...) FILE_IO_NO_FAIL(fwrite, __VA_ARGS__)
// typedefs
typedef uintmax_t bb_counter_t;
typedef long double bb_fp_return_t;
// constants // constants
#define ANSI_ESCAPE_CODES_SUPPORTED `ansi_escape_codes_supported` #define ANSI_ESCAPE_CODES_SUPPORTED `ansi_escape_codes_supported`
@ -31,22 +67,42 @@
#define BIT_DEPTH `bit_depth` #define BIT_DEPTH `bit_depth`
#define IS_SIGNED `is_signed` #define IS_SIGNED `is_signed`
#define CHANNELS `channels` #define CHANNELS `channels`
#define LENGTH `length` #define RUNNING_LENGTH `running_length`
#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 SAMPLE_TYPE uint8_t # define SAMPLE_TYPE uint8_t
#elif BIT_DEPTH >= 16 #elif BIT_DEPTH <= 16
# define ACTUAL_BIT_DEPTH 16
# if IS_SIGNED # if IS_SIGNED
# define SAMPLE_TYPE int16_t # define SAMPLE_TYPE int16_t
# else # else
# define SAMPLE_TYPE uint16_t # define SAMPLE_TYPE uint16_t
# endif # endif
#elif BIT_DEPTH <= 32
# define ACTUAL_BIT_DEPTH 32
# if IS_SIGNED
# define SAMPLE_TYPE int32_t
# else
# define SAMPLE_TYPE uint32_t
# endif
#else
# error "Unsupported bit depth"
# define _ERROR
#endif #endif
#define PRODUCT `wav_product` #ifndef _ERROR
#define GEN_LENGTH `gen_length`
#define PRODUCT `wav_product`ULL
#define GEN_LENGTH `gen_length`ULL
#define INITIAL_TIME `initial_time`
#define LOOP_END `loop_end`ULL
#define LOOP_END_MINUS_1 `loop_end_minus_1`ULL
#define REPEAT_TIMES `repeat_times`ULL
#define FREQUENCY_OF_STATUS_REPORTING 5000 #define FREQUENCY_OF_STATUS_REPORTING 5000
#define SEQUENTIAL_MODE `sequential_mode` #define SEQUENTIAL_MODE `sequential_mode`
@ -57,8 +113,8 @@
#define FP_RETURN_TYPE `fp_return_type` #define FP_RETURN_TYPE `fp_return_type`
#define PRECALCULATED_RATIO `precalculated_ratio` #define PRECALCULATED_RATIO `precalculated_ratio`
#define BIT_DEPTH_LIMITER ((1 << BIT_DEPTH) - 1) #define BIT_DEPTH_LIMITER ((1ULL << ACTUAL_BIT_DEPTH) - 1ULL)
#define PCM_COEFFICIENT ((1 << (BIT_DEPTH - 1)) - 1) #define PCM_COEFFICIENT ((1ULL << (ACTUAL_BIT_DEPTH - 1ULL)) - 1ULL)
#define unsigned_to_signed(x) (x - PCM_COEFFICIENT) #define unsigned_to_signed(x) (x - PCM_COEFFICIENT)
#define signed_to_unsigned(x) (x + PCM_COEFFICIENT) #define signed_to_unsigned(x) (x + PCM_COEFFICIENT)
@ -82,19 +138,19 @@
// function prototypes // function prototypes
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
long double bb_fp_return_t
#else #else
SAMPLE_TYPE SAMPLE_TYPE
#endif #endif
bytebeat(long double time); bytebeat(bb_counter_t time);
// function implementations // function implementations
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
long double bb_fp_return_t
#else #else
SAMPLE_TYPE SAMPLE_TYPE
#endif #endif
bytebeat(long double time) bytebeat(bb_counter_t time)
{ {
#if PRECALCULATED_RATIO #if PRECALCULATED_RATIO
`final_sample_rate_code` `final_sample_rate_code`
@ -109,37 +165,10 @@ bytebeat(long double time)
`bytebeat_contents`; `bytebeat_contents`;
} }
int void
main(void) print_time(uintmax_t length) {
{ const uintmax_t seconds = length / SAMPLE_RATE,
// * log -> welcome samples = length % SAMPLE_RATE;
#if !SILENT_MODE
puts(":: C bytebeat generator runtime unit");
fflush(stdout);
const uintmax_t seconds = LENGTH / SAMPLE_RATE,
samples = LENGTH % SAMPLE_RATE;
printf(
"\n"
"Sample rate: " INT2STR(SAMPLE_RATE) " Hz\n"
"Channels: " INT2STR(CHANNELS)
# if CHANNELS <= 2
" ("
# if CHANNELS == 2
"stereo"
# else
"mono"
# endif
")"
# endif
"\n"
"Bit depth: "
# if !IS_SIGNED
"un"
# endif
"signed " INT2STR(BIT_DEPTH) "-bit\n"
"Duration: ");
if (seconds > 0) if (seconds > 0)
if (seconds >= 3600) if (seconds >= 3600)
@ -158,22 +187,85 @@ main(void)
if (samples > 0) if (samples > 0)
printf("%" PRIuMAX " samples", samples); printf("%" PRIuMAX " samples", samples);
}
int
main(void)
{
// * log -> welcome
#if !SILENT_MODE
puts(":: C bytebeat generator: runtime unit");
fflush(stdout);
printf(
"\n"
"Sample rate: " INT2STR(SAMPLE_RATE) " Hz\n"
"Channels: " INT2STR(CHANNELS)
# if CHANNELS <= 2
" ("
# if CHANNELS == 2
"stereo"
# else
"mono"
# endif
")"
# endif
"\n"
"Bit depth: "
# if !IS_SIGNED
"un"
#endif
"signed " INT2STR(BIT_DEPTH) "-bit"
#if BIT_DEPTH != ACTUAL_BIT_DEPTH
" -> " INT2STR(ACTUAL_BIT_DEPTH) "-bit"
#endif
"\n"
"Duration: ");
print_time(RUNNING_LENGTH);
/*
<<<<<<< HEAD
if (seconds > 0 && samples > 0)
printf(" + ");
if (samples > 0)
printf("%" PRIuMAX " samples", samples);
# if VERBOSE_MODE || SEQUENTIAL_MODE # if VERBOSE_MODE || SEQUENTIAL_MODE
puts("\n"); puts("\n");
# endif # endif
*/
#if REPEAT_TIMES > 0
printf(", %llu times -> ", REPEAT_TIMES + 1);
print_time(RUNNING_LENGTH * (REPEAT_TIMES + 1));
#endif
#if INITIAL_TIME != 0
printf("\nStart time: ");
print_time(INITIAL_TIME);
#endif
#if VERBOSE_MODE || SEQUENTIAL_MODE
puts("\n");
#endif
fflush(stdout); fflush(stdout);
#endif #endif
// * write WAVE headers // * write WAVE headers
// 0. log // 0. log
#if !SILENT_MODE && SEQUENTIAL_MODE #if !SILENT_MODE && VERBOSE_MODE
puts("Writing WAVE headers..."); puts("Writing WAVE headers...");
#endif #endif
// 1. open file // 1. open file
FILE* output_file = fopen(OUTPUT_FILE, "wb"); FILE* output_file = fopen(OUTPUT_FILE,
"wb"
#if REPEAT_TIMES > 0 && SEQUENTIAL_MODE
"+"
#endif
);
if (output_file == NULL) { if (output_file == NULL) {
fflush(stdout); fflush(stdout);
perror("fopen"); perror("fopen");
@ -181,23 +273,23 @@ main(void)
} }
// 2. prepare variables // 2. prepare variables
uint32_t buffer_size = PRODUCT, const uint32_t header_size =
file_length = 3 * 4 /* 3 strings of 4 characters: "WAVE", "fmt ", "data" */ +
/* file length without "RIFF" chunk */ 4 * 4 /* 4 uint32_t values */ +
3 * 4 /* 3 strings of 4 characters: "WAVE", "fmt ", "data" */ + 4 * 2 /* 4 uint16_t values */;
4 * 4 /* 4 uint32_t values */ +
4 * 2 /* 4 uint16_t values */ + uint32_t buffer_size = PRODUCT * (REPEAT_TIMES + 1),
PRODUCT /* sample data */, file_length = header_size + buffer_size,
fmt_data_length = 16 /* <-- fmt_data_length = 16 /* <--
* length of format data before this value * length of format data before this value
* in the file format structure * in the file format structure
*/, */,
sample_rate = SAMPLE_RATE, sample_rate = SAMPLE_RATE,
byte_rate = (SAMPLE_RATE * BIT_DEPTH * CHANNELS) / BITS_PER_BYTE; byte_rate = (SAMPLE_RATE * ACTUAL_BIT_DEPTH * CHANNELS) / BITS_PER_BYTE;
uint16_t fmt_type = 1, // format type is PCM uint16_t fmt_type = 1, // format type is PCM
channels = CHANNELS, channels = CHANNELS,
block_align = (BIT_DEPTH * CHANNELS) / BITS_PER_BYTE, block_align = (ACTUAL_BIT_DEPTH * CHANNELS) / BITS_PER_BYTE,
bit_depth = BIT_DEPTH > BITS_PER_BYTE ? BIT_DEPTH : BITS_PER_BYTE; bit_depth = ACTUAL_BIT_DEPTH;
// 3. write headers // 3. write headers
// <L = Little-endian or B = Big-endian> : <name> : <bytes of field> // <L = Little-endian or B = Big-endian> : <name> : <bytes of field>
@ -236,22 +328,23 @@ main(void)
size_t calc_block_size = BLOCK_SIZE; size_t calc_block_size = BLOCK_SIZE;
ALLOCATE_MEMORY(calc_block_size) ALLOCATE_MEMORY(calc_block_size)
#else #else
ALLOCATE_MEMORY(PRODUCT) ALLOCATE_MEMORY(GEN_LENGTH)
#endif #endif
#if SEQUENTIAL_MODE const uintmax_t bit_depth_limiter = BIT_DEPTH_LIMITER
const uintmax_t gen_length_minus_1 = GEN_LENGTH - 1,
bit_depth_limiter = BIT_DEPTH_LIMITER
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
+ 1 + 1
#endif
#if BIT_DEPTH < BITS_PER_BYTE
,
bit_depth_stretch = BIT_DEPTH / BITS_PER_BYTE
#endif #endif
; ;
size_t time = 0; #if BIT_DEPTH != ACTUAL_BIT_DEPTH
const long double bit_depth_stretch =
((long double) BIT_DEPTH) / ACTUAL_BIT_DEPTH;
#endif
#if SEQUENTIAL_MODE
size_t time = INITIAL_TIME;
for (size_t seq = 0; seq < MAX; seq++) { for (size_t seq = 0; seq < MAX; seq++) {
if ((time + BLOCK_SIZE) >= GEN_LENGTH) if ((time + BLOCK_SIZE) >= GEN_LENGTH)
calc_block_size = GEN_LENGTH - time; calc_block_size = GEN_LENGTH - time;
@ -259,34 +352,34 @@ main(void)
// * bytebeat generating loop // * bytebeat generating loop
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
for (size_t idx = 0; idx < BLOCK_SIZE && time < GEN_LENGTH; idx++, for (size_t idx = 0; idx < BLOCK_SIZE && time < LOOP_END; idx++,
time++) { time++) {
#else #else
for (size_t time = 0; time < GEN_LENGTH; time++) { for (size_t time = INITIAL_TIME; time < LOOP_END; time++) {
#endif #endif
// 1. generate audio data // 1. generate audio data
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
long double bytebeat_res = bb_fp_return_t bytebeat_res =
floor(bytebeat(floor((long double) time))); floor(bytebeat(floor((bb_counter_t) time)));
#elif IS_SIGNED #elif IS_SIGNED
intmax_t bytebeat_res = intmax_t bytebeat_res =
(intmax_t) bytebeat(floor((long double) time)); (intmax_t) bytebeat(floor((bb_counter_t) time));
#else #else
uintmax_t bytebeat_res = uintmax_t bytebeat_res =
(uintmax_t) bytebeat(floor((long double) time)); (uintmax_t) bytebeat(floor((bb_counter_t) time));
#endif #endif
// 2. if signed, then wrap up to unsigned // 2. if signed and bit depth <= 8, then wrap up to unsigned
#if IS_SIGNED #if IS_SIGNED && (BIT_DEPTH <= 8)
bytebeat_res = signed_to_unsigned(bytebeat_res); bytebeat_res = signed_to_unsigned(bytebeat_res);
#endif #endif
// 3. convert audio data to sample // 3. convert audio data to sample
SAMPLE_TYPE sample_res = (SAMPLE_TYPE) SAMPLE_TYPE sample_res = (SAMPLE_TYPE)
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
fmodl(bytebeat_res, bit_depth_limiter); fmod(bytebeat_res, BIT_DEPTH_LIMITER);
#else #else
((uintmax_t) bytebeat_res & bit_depth_limiter); ((uintmax_t) bytebeat_res & BIT_DEPTH_LIMITER);
#endif #endif
// 4. if bit depth is less than BITS_PER_BYTE, stretch it // 4. if bit depth is less than BITS_PER_BYTE, stretch it
@ -296,26 +389,28 @@ main(void)
#endif #endif
// 5. save sample into buffer // 5. save sample into buffer
buffer[
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
buffer[idx] = sample_res; idx
#else #else
buffer[time] = sample_res; time
#endif #endif
] = sample_res;
// 6. log // 6. log
#if VERBOSE_MODE #if VERBOSE_MODE
if (time % FREQUENCY_OF_STATUS_REPORTING == 0 || if (time % FREQUENCY_OF_STATUS_REPORTING == 0 ||
time >= gen_length_minus_1 /* or if writing last sample */) { time >= LOOP_END_MINUS_1 /* or if writing last sample */) {
printf( printf(
"%sremaining samples = %18" PRIuMAX " (%3.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,
gen_length_minus_1 - time, (uintmax_t) (LOOP_END_MINUS_1 - time),
((long double) time * 100) / (long double) GEN_LENGTH ((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
); );
@ -346,7 +441,7 @@ main(void)
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
calc_block_size, calc_block_size,
#else #else
PRODUCT, GEN_LENGTH,
#endif #endif
output_file); output_file);
#if SEQUENTIAL_MODE #if SEQUENTIAL_MODE
@ -357,6 +452,47 @@ main(void)
} }
#endif #endif
#if REPEAT_TIMES > 0
// * repeat as much as needed
# if !SILENT_MODE
puts(
# if SEQUENTIAL_MODE
"\n"
# endif
"Repeating...");
# endif
for (size_t counter = 0; counter < REPEAT_TIMES; counter++) {
# if SEQUENTIAL_MODE
file_offset_t position_read = header_size;
calc_block_size = BLOCK_SIZE;
for (size_t seq = 0, time = 0; seq < MAX; seq++, time += BLOCK_SIZE) {
bool end = false;
if ((time + BLOCK_SIZE) >= GEN_LENGTH) {
calc_block_size = GEN_LENGTH - time;
end = true;
}
FSEEK_NO_FAIL(output_file, position_read, SEEK_SET);
FREAD_NO_FAIL(buffer, sizeof(SAMPLE_TYPE), calc_block_size,
output_file);
FSEEK_NO_FAIL(output_file, 0, SEEK_END);
FWRITE_NO_FAIL(buffer, sizeof(SAMPLE_TYPE), calc_block_size,
output_file);
if (end)
break;
position_read += calc_block_size;
}
# else
fwrite_le(buffer, sizeof(SAMPLE_TYPE), GEN_LENGTH, output_file);
# endif
}
#endif
// * free allocated heap // * free allocated heap
free(buffer); free(buffer);
@ -366,7 +502,7 @@ main(void)
// * end of program // * end of program
#if !SILENT_MODE #if !SILENT_MODE
puts( puts(
# if SEQUENTIAL_MODE && VERBOSE_MODE # if SEQUENTIAL_MODE && VERBOSE_MODE && REPEAT_TIMES == 0
"\n" "\n"
# endif # endif
"Done!"); "Done!");
@ -374,3 +510,5 @@ main(void)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#endif /* _ERROR */