Merge branch 'WIP'
This commit is contained in:
commit
2f0975287f
@ -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
5
samples/beeper.c
Normal 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
3
samples/cykstep.c
Normal 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
7
samples/melody.c
Normal 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
6
samples/pwm-sierpinski.c
Normal 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
|
304
src/template.c
304
src/template.c
@ -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 */
|
||||||
|
Loading…
Reference in New Issue
Block a user