1
0

Merge branch 'WIP' into optimization/float_type

This commit is contained in:
Intel A80486DX2-66 2024-07-14 00:34:55 +03:00
commit 5b2dcd0cf4
Signed by: 80486DX2-66
GPG Key ID: 83631EF27054609B
8 changed files with 336 additions and 121 deletions

View File

@ -6,6 +6,11 @@
> **Information** > **Information**
> You're on the branch where the program uses only type `float` for floating point values. > You're on the branch where the program uses only type `float` for floating point values.
Dual-licensed under the [Unlicense](http://unlicense.org) (`LICENSE`) or Creative Commons Zero 1.0 Universal (`COPYING`). **Description:** a bytebeat rendering engine in C (no support of JavaScript
bytebeat yet)
**Description:** a bytebeat rendering engine in C (no support of JavaScript bytebeat yet) ## License
Dual-licensed under the [Unlicense](http://unlicense.org)
([`LICENSE`](LICENSE)) and Creative Commons Zero 1.0 Universal
([`COPYING`](COPYING)).

View File

@ -10,8 +10,12 @@ from shlex import split as command_line_split
from shutil import which from shutil import which
from sys import stdin, stdout, exit from sys import stdin, stdout, exit
from typing import Dict, Union from typing import Dict, Union
import re
import subprocess import subprocess
# Definitions
BITS_PER_BYTE = 8
# Paths # Paths
PATHS = { PATHS = {
"src_dir": "src", "src_dir": "src",
@ -105,6 +109,26 @@ OUTPUT_FILE = fetch("OUTPUT_FILE")
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",
"no_return",
)
DEFAULT_ARGS = {
"sample_rate": 8000,
"final_sample_rate": 0,
"bit_depth": 8,
"signed": False,
"channels": 1,
"no_return": 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
@ -117,37 +141,45 @@ 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 file path (default is `output.wav`") help="specify output 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=int, parser.add_argument("-t", "--seconds", default=None, type=int,
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", "--no-return", default=False, action="store_true", parser.add_argument("-S", "--skip-first", default=None, type=str,
help="skip first `A` seconds and `B` samples: in format `As`, `B` or "
"`AsB` : default = 0")
parser.add_argument("-k", "--repeat", default=0, type=int,
help="how many times to repeat the bytebeat : "
"default = 0")
parser.add_argument("-a", "--no-return", default=None, action="store_true",
help="do not insert return statement before the code") help="do not insert return statement before the code")
parser.add_argument("-u", "--mode", default="sequential", type=str, 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,
@ -167,7 +199,42 @@ if __name__ == "__main__":
if not bytebeat_contents: if not bytebeat_contents:
print("No valid contents") print("No valid contents")
raise SystemExit exit(1)
# - 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("=")]
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
makedirs(PATHS["build_dir"], exist_ok=True) makedirs(PATHS["build_dir"], exist_ok=True)
@ -181,7 +248,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;"
@ -191,7 +258,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
@ -201,7 +269,7 @@ if __name__ == "__main__":
if seconds_specified and args.seconds < 0: if seconds_specified and args.seconds < 0:
print("CLI: Count of seconds can't be less than zero.") print("CLI: Count of seconds can't be less than zero.")
raise SystemExit exit(1)
if no_seconds and samples_specified: if no_seconds and samples_specified:
samples = args.samples samples = args.samples
@ -214,27 +282,53 @@ if __name__ == "__main__":
continue continue
else: else:
print("CLI: Incorrect seconds/samples length format.") print("CLI: Incorrect seconds/samples length format.")
raise SystemExit exit(1)
break break
if samples <= 0: if samples <= 0:
print("CLI: Count of samples should be greater than zero.") print("CLI: Count of samples should be greater than zero.")
raise SystemExit exit(1)
if args.mode != "sequential" and args.mode != "instant": if args.mode != "sequential" and args.mode != "instant":
print("Invalid mode '%s'" % args.mode) print("Invalid mode '%s'" % args.mode)
raise SystemExit exit(1)
gen_length = args.channels * samples
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 not args.skip_first is None:
encountered_s = False
for character in args.skip_first:
if character.isdigit() or character == "s" and not encountered_s:
if character == "s":
encountered_s = True
else:
print(f"Invalid --skip-first format: `{args.skip_first}`")
exit(1)
skip_first = \
[int(x) if x.isdigit() 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]
else:
skip_first_samples = 0
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)
rewrite_file(PATHS["substitute"], substitute_vars({ rewrite_file(PATHS["substitute"], 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,
@ -243,8 +337,12 @@ 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,
"wav_product": gen_length * (args.bit_depth // 8), "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),
"gen_length": gen_length, "gen_length": gen_length,
"sequential_mode": args.mode == "sequential", "sequential_mode": args.mode == "sequential",
"block_size": args.block_size, "block_size": args.block_size,

View File

@ -1,24 +1,23 @@
#ifndef _FWRITE_LE_H
#define _FWRITE_LE_H
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifndef _FWRITE_LE_H
#define _FWRITE_LE_H
#define FWRITE_LE_NO_MODIFICATION 0 #define FWRITE_LE_NO_MODIFICATION 0
#define DETECTED_LITTLE_ENDIAN 0 #define DETECTED_LITTLE_ENDIAN 0
#define DETECTED_BIG_ENDIAN 1 #define DETECTED_BIG_ENDIAN 1
#define UNSUPPORTED_ENDIANNESS -1 #define UNSUPPORTED_ENDIANNESS -1
#define ORDER_NATIVE_U32 0x01234567
#define ORDER_LITTLE_ENDIAN_S4 "\x67\x45\x23\x01"
#define ORDER_BIG_ENDIAN_S4 "\x01\x23\x45\x67"
#define ifeq_b32_ret(lhs, rhs, value) if (!memcmp(lhs, rhs, 4)) return value;
int detect_endianness(void); int detect_endianness(void);
size_t fwrite_le(void* ptr, size_t size, size_t count, FILE* stream); size_t fwrite_le(
#if FWRITE_LE_NO_MODIFICATION
const
#endif
void* ptr, size_t size, size_t count, FILE* stream);
void reorder_le_be( void reorder_le_be(
#if FWRITE_LE_NO_MODIFICATION #if FWRITE_LE_NO_MODIFICATION
uint8_t* dest, uint8_t* src, uint8_t* dest, uint8_t* src,

5
samples/beeper.c Normal file
View File

@ -0,0 +1,5 @@
// RENDER PARAMETERS: sample_rate = 44100, no_return
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)))

5
samples/melody.c Normal file
View File

@ -0,0 +1,5 @@
// RENDER PARAMETERS: sample_rate = 44100, no_return
const long double array[] = {1, 1.25, 1.5, 2};
long double v = array[3 & (t >> 13)];
return (long double) t * v / 3.1415926535;

View File

@ -1,5 +1,10 @@
#include "fwrite_le.h" #include "fwrite_le.h"
#define ORDER_NATIVE_U32 0x01234567
#define ORDER_LITTLE_ENDIAN_S4 "\x67\x45\x23\x01"
#define ORDER_BIG_ENDIAN_S4 "\x01\x23\x45\x67"
#define ifeq_b32_ret(lhs, rhs, value) if (!memcmp(lhs, rhs, 4)) return value;
int detect_endianness(void) { int detect_endianness(void) {
volatile uint32_t native_order_value = ORDER_NATIVE_U32; volatile uint32_t native_order_value = ORDER_NATIVE_U32;
uint8_t* as_bytes = (uint8_t*)&native_order_value; uint8_t* as_bytes = (uint8_t*)&native_order_value;
@ -37,7 +42,11 @@ void reorder_le_be(
} }
} }
size_t fwrite_le(void* ptr, size_t size, size_t count, FILE* stream) { size_t fwrite_le(
#if FWRITE_LE_NO_MODIFICATION
const
#endif
void* ptr, size_t size, size_t count, FILE* stream) {
/* /*
* warning: this function modifies `void* ptr` by default! * warning: this function modifies `void* ptr` by default!
* (if FWRITE_LE_NO_MODIFICATION in the header is 0) * (if FWRITE_LE_NO_MODIFICATION in the header is 0)
@ -56,9 +65,9 @@ size_t fwrite_le(void* ptr, size_t size, size_t count, FILE* stream) {
// 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 = calloc(bytes_count, sizeof(uint8_t)); uint8_t* bytes = malloc(bytes_count, sizeof(uint8_t));
if (bytes == NULL) { if (bytes == NULL) {
perror("calloc"); perror("malloc");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
memcpy(bytes, ptr, bytes_count); memcpy(bytes, ptr, bytes_count);

View File

@ -9,6 +9,10 @@
#include "`fwrite_le`" #include "`fwrite_le`"
// 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`
@ -29,20 +33,41 @@
#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`
#if BIT_DEPTH <= 8 #define BITS_PER_BYTE 8
#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`
@ -53,8 +78,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)
@ -78,19 +103,19 @@
// function prototypes // function prototypes
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
float bb_fp_return_t
#else #else
SAMPLE_TYPE SAMPLE_TYPE
#endif #endif
bytebeat(float time); bytebeat(bb_counter_t time);
// function implementations // function implementations
#if FP_RETURN_TYPE #if FP_RETURN_TYPE
float bb_fp_return_t
#else #else
SAMPLE_TYPE SAMPLE_TYPE
#endif #endif
bytebeat(float time) bytebeat(bb_counter_t time)
{ {
#if PRECALCULATED_RATIO #if PRECALCULATED_RATIO
`final_sample_rate_code` `final_sample_rate_code`
@ -105,18 +130,37 @@ bytebeat(float time)
`bytebeat_contents`; `bytebeat_contents`;
} }
void
print_time(uintmax_t length) {
const uintmax_t seconds = length / SAMPLE_RATE,
samples = length % SAMPLE_RATE;
if (seconds > 0)
if (seconds >= 3600)
printf(
"%" PRIuMAX ":%02" PRIuMAX ":%02" PRIuMAX,
seconds / 3600,
(seconds / 60) % 60,
seconds % 60);
else if (seconds >= 60)
printf("%" PRIuMAX ":%02" PRIuMAX, seconds / 60, seconds % 60);
else
printf("%" PRIuMAX " seconds", seconds);
if (seconds > 0 && samples > 0)
printf(" + ");
if (samples > 0)
printf("%" PRIuMAX " samples", samples);
}
int int
main(void) main(void)
{ {
// * log -> welcome // * log -> welcome
#if !SILENT_MODE #if !SILENT_MODE
puts(":: C bytebeat generator runtime unit"); puts(":: C bytebeat generator: runtime unit");
fflush(stdout); fflush(stdout);
#endif
#if !SILENT_MODE
const uintmax_t seconds = LENGTH / SAMPLE_RATE,
samples = LENGTH % SAMPLE_RATE;
printf( printf(
"\n" "\n"
@ -136,26 +180,24 @@ main(void)
#if !IS_SIGNED #if !IS_SIGNED
"un" "un"
#endif #endif
"signed " INT2STR(BIT_DEPTH) "-bit\n" "signed " INT2STR(BIT_DEPTH) "-bit"
#if BIT_DEPTH != ACTUAL_BIT_DEPTH
" -> " INT2STR(ACTUAL_BIT_DEPTH) "-bit"
#endif
"\n"
"Duration: "); "Duration: ");
if (seconds > 0) print_time(RUNNING_LENGTH);
if (seconds >= 3600)
printf(
"%" PRIuMAX ":%02" PRIuMAX ":%02" PRIuMAX,
seconds / 3600,
(seconds / 60) % 60,
seconds % 60);
else if (seconds >= 60)
printf("%" PRIuMAX ":%02" PRIuMAX, seconds / 60, seconds % 60);
else
printf("%" PRIuMAX " seconds", seconds);
if (seconds > 0 && samples > 0) #if REPEAT_TIMES > 0
printf(" + "); printf(", %llu times -> ", REPEAT_TIMES + 1);
print_time(RUNNING_LENGTH * (REPEAT_TIMES + 1));
#endif
if (samples > 0) #if INITIAL_TIME != 0
printf("%" PRIuMAX " samples", samples); printf("\nStart time: ");
print_time(INITIAL_TIME);
#endif
#if VERBOSE_MODE || SEQUENTIAL_MODE #if VERBOSE_MODE || SEQUENTIAL_MODE
puts("\n"); puts("\n");
@ -166,39 +208,41 @@ main(void)
// * write WAVE headers // * write WAVE headers
// 0. log // 0. log
#if 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");
return 1; return EXIT_FAILURE;
} }
// 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" */ +
4 * 4 /* 4 strings of 4 characters */ + 4 * 4 /* 4 uint32_t values */ +
5 * 4 /* 5 uint32_t values */ + 4 * 2 /* 4 uint16_t values */;
4 * 2 /* 4 uint16_t values */ +
PRODUCT /* sample data */
/* subtract Subchunk2 headers: */ uint32_t buffer_size = PRODUCT * (REPEAT_TIMES + 1),
- 4 /* a string of 4 characters */ file_length = header_size + buffer_size,
- 4 /* a uint32_t value */,
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) / 8; 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) / 8, block_align = (ACTUAL_BIT_DEPTH * CHANNELS) / BITS_PER_BYTE,
bit_depth = BIT_DEPTH > 8 ? BIT_DEPTH : 8; 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>
@ -221,8 +265,11 @@ main(void)
size_t BLOCK_SIZE = BLOCK_SIZE_BYTES / (sizeof(SAMPLE_TYPE) / size_t BLOCK_SIZE = BLOCK_SIZE_BYTES / (sizeof(SAMPLE_TYPE) /
sizeof(uint8_t)); sizeof(uint8_t));
if (BLOCK_SIZE < 1) { if (BLOCK_SIZE < 1) {
printf("The block size " INT2STR(BLOCK_SIZE_BYTES) " is too small, " # if !SILENT_MODE
"should be at least %" PRIuMAX " bytes\n", MINIMUM_BLOCK_SIZE); fprintf(stderr, "The block size " INT2STR(BLOCK_SIZE_BYTES) " is too "
"small, should be at least %" PRIuMAX " bytes\n",
MINIMUM_BLOCK_SIZE);
# endif
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -234,22 +281,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 < 8
,
bit_depth_stretch = ((float) BIT_DEPTH) / 8.L
#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;
@ -257,62 +305,64 @@ 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
float bytebeat_res = bb_fp_return_t bytebeat_res =
floorf(bytebeat(floorf((float) 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(floorf((float) time)); (intmax_t) bytebeat(floor((bb_counter_t) time));
#else #else
uintmax_t bytebeat_res = uintmax_t bytebeat_res =
(uintmax_t) bytebeat(floorf((float) 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 8, stretch it // 4. if bit depth is less than BITS_PER_BYTE, stretch it
#if BIT_DEPTH < 8 #if BIT_DEPTH < BITS_PER_BYTE
sample_res = (SAMPLE_TYPE) sample_res = (SAMPLE_TYPE)
((float) sample_res * bit_depth_stretch); ((float) sample_res * bit_depth_stretch);
#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, LOOP_END_MINUS_1 - time,
((float) time * 100) / (float) 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
@ -344,7 +394,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
@ -355,6 +405,45 @@ 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
off_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;
}
fseeko(output_file, position_read, SEEK_SET);
fread(buffer, sizeof(SAMPLE_TYPE), calc_block_size, output_file);
fseeko(output_file, 0, SEEK_END);
fwrite(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);
@ -364,7 +453,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!");
@ -372,3 +461,5 @@ main(void)
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
#endif /* _ERROR */