Merge branch 'WIP' into optimization/float_type
This commit is contained in:
commit
5b2dcd0cf4
@ -6,6 +6,11 @@
|
||||
> **Information**
|
||||
> 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)).
|
||||
|
@ -10,8 +10,12 @@ from shlex import split as command_line_split
|
||||
from shutil import which
|
||||
from sys import stdin, stdout, exit
|
||||
from typing import Dict, Union
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
# Definitions
|
||||
BITS_PER_BYTE = 8
|
||||
|
||||
# Paths
|
||||
PATHS = {
|
||||
"src_dir": "src",
|
||||
@ -105,6 +109,26 @@ OUTPUT_FILE = fetch("OUTPUT_FILE")
|
||||
if extra := fetch("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_unavailable = lambda cmd: which(cmd) is None
|
||||
|
||||
@ -117,37 +141,45 @@ if __name__ == "__main__":
|
||||
parser.add_argument("file", type=str,
|
||||
help="bytebeat formula file (use `-` to read from stdin)")
|
||||
parser.add_argument("-o", "--output", default="output.wav", type=str,
|
||||
help="specify output file path (default is `output.wav`")
|
||||
parser.add_argument("-r", "--sample-rate", default=8000, type=int,
|
||||
help="specify output file path : default is `output.wav`")
|
||||
parser.add_argument("-r", "--sample-rate", default=None, type=int,
|
||||
help="sample rate (Hz)")
|
||||
parser.add_argument("-p", "--final-sample-rate", default=None, type=int,
|
||||
help="convert the output to a different sample rate (usually one that "
|
||||
"is set in the system, to improve sound quality) during generation "
|
||||
"(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")
|
||||
parser.add_argument("-s", "--signed", default=False, action="store_true",
|
||||
parser.add_argument("-s", "--signed", default=None, action="store_true",
|
||||
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,
|
||||
action="store_true",
|
||||
help="precalculate sample ratio to speed up rendering (may produce "
|
||||
"inaccurate results")
|
||||
"inaccurate results)")
|
||||
parser.add_argument("-m", "--faster-sample-ratio-math", default=False,
|
||||
action="store_true",
|
||||
help="faster sample ratio math (implies argument -R)")
|
||||
parser.add_argument("-f", "--floating-point", default=False,
|
||||
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")
|
||||
parser.add_argument("-t", "--seconds", default=None, type=int,
|
||||
help="length in seconds (samples = sample rate * seconds) : "
|
||||
"default = 30 seconds")
|
||||
parser.add_argument("-l", "--samples", default=None, type=int,
|
||||
help="length in samples (adds to `-t`; supports negative numbers) : "
|
||||
"default = +0 samples")
|
||||
parser.add_argument("-a", "--no-return", default=False, action="store_true",
|
||||
"default = seconds + 0 samples")
|
||||
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")
|
||||
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 "
|
||||
"recommended, since the whole result would be stored in RAM)")
|
||||
parser.add_argument("-n", "--block-size", default=65536, type=int,
|
||||
@ -167,7 +199,42 @@ if __name__ == "__main__":
|
||||
|
||||
if not bytebeat_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
|
||||
makedirs(PATHS["build_dir"], exist_ok=True)
|
||||
@ -181,7 +248,7 @@ if __name__ == "__main__":
|
||||
final_sample_rate_code = ""
|
||||
if args.faster_sample_ratio_math:
|
||||
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:
|
||||
sample_rate_ratio = args.sample_rate / args.final_sample_rate
|
||||
final_sample_rate_code = f"time *= {sample_rate_ratio}L;"
|
||||
@ -191,7 +258,8 @@ if __name__ == "__main__":
|
||||
args.sample_rate = args.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
|
||||
while True:
|
||||
no_seconds = args.seconds is None or args.seconds == 0
|
||||
@ -201,7 +269,7 @@ if __name__ == "__main__":
|
||||
|
||||
if seconds_specified and args.seconds < 0:
|
||||
print("CLI: Count of seconds can't be less than zero.")
|
||||
raise SystemExit
|
||||
exit(1)
|
||||
|
||||
if no_seconds and samples_specified:
|
||||
samples = args.samples
|
||||
@ -214,27 +282,53 @@ if __name__ == "__main__":
|
||||
continue
|
||||
else:
|
||||
print("CLI: Incorrect seconds/samples length format.")
|
||||
raise SystemExit
|
||||
exit(1)
|
||||
break
|
||||
|
||||
if samples <= 0:
|
||||
print("CLI: Count of samples should be greater than zero.")
|
||||
raise SystemExit
|
||||
exit(1)
|
||||
|
||||
if args.mode != "sequential" and args.mode != "instant":
|
||||
print("Invalid mode '%s'" % args.mode)
|
||||
raise SystemExit
|
||||
|
||||
gen_length = args.channels * samples
|
||||
exit(1)
|
||||
|
||||
ansi_escape_codes_supported = args.color == "auto" and stdout_atty or \
|
||||
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({
|
||||
"bytebeat_contents": bytebeat_contents,
|
||||
"output_file": C_str_repr(args.output),
|
||||
"sample_rate": \
|
||||
value if (value := args.final_sample_rate) else args.sample_rate,
|
||||
"sample_rate": actual_sample_rate,
|
||||
"original_sample_rate": original_sample_rate,
|
||||
"final_sample_rate_code": final_sample_rate_code,
|
||||
"bit_depth": args.bit_depth,
|
||||
@ -243,8 +337,12 @@ if __name__ == "__main__":
|
||||
"faster_sample_ratio_math": args.precalculate_ratio,
|
||||
"fp_return_type": args.floating_point,
|
||||
"channels": args.channels,
|
||||
"length": samples,
|
||||
"wav_product": gen_length * (args.bit_depth // 8),
|
||||
"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),
|
||||
"gen_length": gen_length,
|
||||
"sequential_mode": args.mode == "sequential",
|
||||
"block_size": args.block_size,
|
||||
|
@ -1,24 +1,23 @@
|
||||
#ifndef _FWRITE_LE_H
|
||||
#define _FWRITE_LE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef _FWRITE_LE_H
|
||||
#define _FWRITE_LE_H
|
||||
|
||||
#define FWRITE_LE_NO_MODIFICATION 0
|
||||
|
||||
#define DETECTED_LITTLE_ENDIAN 0
|
||||
#define DETECTED_BIG_ENDIAN 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);
|
||||
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(
|
||||
#if FWRITE_LE_NO_MODIFICATION
|
||||
uint8_t* dest, uint8_t* src,
|
||||
|
5
samples/beeper.c
Normal file
5
samples/beeper.c
Normal 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
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)))
|
5
samples/melody.c
Normal file
5
samples/melody.c
Normal 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;
|
@ -1,5 +1,10 @@
|
||||
#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) {
|
||||
volatile uint32_t native_order_value = ORDER_NATIVE_U32;
|
||||
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!
|
||||
* (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
|
||||
size_t bytes_count = size * count;
|
||||
#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) {
|
||||
perror("calloc");
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memcpy(bytes, ptr, bytes_count);
|
||||
|
261
src/template.c
261
src/template.c
@ -9,6 +9,10 @@
|
||||
|
||||
#include "`fwrite_le`"
|
||||
|
||||
// typedefs
|
||||
typedef uintmax_t bb_counter_t;
|
||||
typedef long double bb_fp_return_t;
|
||||
|
||||
// constants
|
||||
#define ANSI_ESCAPE_CODES_SUPPORTED `ansi_escape_codes_supported`
|
||||
|
||||
@ -29,20 +33,41 @@
|
||||
#define BIT_DEPTH `bit_depth`
|
||||
#define IS_SIGNED `is_signed`
|
||||
#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
|
||||
#elif BIT_DEPTH >= 16
|
||||
#elif BIT_DEPTH <= 16
|
||||
# define ACTUAL_BIT_DEPTH 16
|
||||
|
||||
# if IS_SIGNED
|
||||
# define SAMPLE_TYPE int16_t
|
||||
# else
|
||||
# define SAMPLE_TYPE uint16_t
|
||||
# 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
|
||||
|
||||
#define PRODUCT `wav_product`
|
||||
#define GEN_LENGTH `gen_length`
|
||||
#ifndef _ERROR
|
||||
|
||||
#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 SEQUENTIAL_MODE `sequential_mode`
|
||||
@ -53,8 +78,8 @@
|
||||
#define FP_RETURN_TYPE `fp_return_type`
|
||||
#define PRECALCULATED_RATIO `precalculated_ratio`
|
||||
|
||||
#define BIT_DEPTH_LIMITER ((1 << BIT_DEPTH) - 1)
|
||||
#define PCM_COEFFICIENT ((1 << (BIT_DEPTH - 1)) - 1)
|
||||
#define BIT_DEPTH_LIMITER ((1ULL << ACTUAL_BIT_DEPTH) - 1ULL)
|
||||
#define PCM_COEFFICIENT ((1ULL << (ACTUAL_BIT_DEPTH - 1ULL)) - 1ULL)
|
||||
#define unsigned_to_signed(x) (x - PCM_COEFFICIENT)
|
||||
#define signed_to_unsigned(x) (x + PCM_COEFFICIENT)
|
||||
|
||||
@ -78,19 +103,19 @@
|
||||
|
||||
// function prototypes
|
||||
#if FP_RETURN_TYPE
|
||||
float
|
||||
bb_fp_return_t
|
||||
#else
|
||||
SAMPLE_TYPE
|
||||
#endif
|
||||
bytebeat(float time);
|
||||
bytebeat(bb_counter_t time);
|
||||
|
||||
// function implementations
|
||||
#if FP_RETURN_TYPE
|
||||
float
|
||||
bb_fp_return_t
|
||||
#else
|
||||
SAMPLE_TYPE
|
||||
#endif
|
||||
bytebeat(float time)
|
||||
bytebeat(bb_counter_t time)
|
||||
{
|
||||
#if PRECALCULATED_RATIO
|
||||
`final_sample_rate_code`
|
||||
@ -105,18 +130,37 @@ bytebeat(float time)
|
||||
`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
|
||||
main(void)
|
||||
{
|
||||
// * log -> welcome
|
||||
#if !SILENT_MODE
|
||||
puts(":: C bytebeat generator runtime unit");
|
||||
puts(":: C bytebeat generator: runtime unit");
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
#if !SILENT_MODE
|
||||
const uintmax_t seconds = LENGTH / SAMPLE_RATE,
|
||||
samples = LENGTH % SAMPLE_RATE;
|
||||
|
||||
printf(
|
||||
"\n"
|
||||
@ -136,26 +180,24 @@ main(void)
|
||||
#if !IS_SIGNED
|
||||
"un"
|
||||
#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: ");
|
||||
|
||||
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);
|
||||
print_time(RUNNING_LENGTH);
|
||||
|
||||
if (seconds > 0 && samples > 0)
|
||||
printf(" + ");
|
||||
#if REPEAT_TIMES > 0
|
||||
printf(", %llu times -> ", REPEAT_TIMES + 1);
|
||||
print_time(RUNNING_LENGTH * (REPEAT_TIMES + 1));
|
||||
#endif
|
||||
|
||||
if (samples > 0)
|
||||
printf("%" PRIuMAX " samples", samples);
|
||||
#if INITIAL_TIME != 0
|
||||
printf("\nStart time: ");
|
||||
print_time(INITIAL_TIME);
|
||||
#endif
|
||||
|
||||
#if VERBOSE_MODE || SEQUENTIAL_MODE
|
||||
puts("\n");
|
||||
@ -166,39 +208,41 @@ main(void)
|
||||
|
||||
// * write WAVE headers
|
||||
// 0. log
|
||||
#if SEQUENTIAL_MODE
|
||||
#if !SILENT_MODE && VERBOSE_MODE
|
||||
puts("Writing WAVE headers...");
|
||||
#endif
|
||||
|
||||
// 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) {
|
||||
fflush(stdout);
|
||||
perror("fopen");
|
||||
return 1;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 2. prepare variables
|
||||
uint32_t buffer_size = PRODUCT,
|
||||
file_length =
|
||||
4 * 4 /* 4 strings of 4 characters */ +
|
||||
5 * 4 /* 5 uint32_t values */ +
|
||||
4 * 2 /* 4 uint16_t values */ +
|
||||
PRODUCT /* sample data */
|
||||
const uint32_t header_size =
|
||||
3 * 4 /* 3 strings of 4 characters: "WAVE", "fmt ", "data" */ +
|
||||
4 * 4 /* 4 uint32_t values */ +
|
||||
4 * 2 /* 4 uint16_t values */;
|
||||
|
||||
/* subtract Subchunk2 headers: */
|
||||
- 4 /* a string of 4 characters */
|
||||
- 4 /* a uint32_t value */,
|
||||
uint32_t buffer_size = PRODUCT * (REPEAT_TIMES + 1),
|
||||
file_length = header_size + buffer_size,
|
||||
fmt_data_length = 16 /* <--
|
||||
* length of format data before this value
|
||||
* in the file format structure
|
||||
*/,
|
||||
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
|
||||
channels = CHANNELS,
|
||||
block_align = (BIT_DEPTH * CHANNELS) / 8,
|
||||
bit_depth = BIT_DEPTH > 8 ? BIT_DEPTH : 8;
|
||||
block_align = (ACTUAL_BIT_DEPTH * CHANNELS) / BITS_PER_BYTE,
|
||||
bit_depth = ACTUAL_BIT_DEPTH;
|
||||
|
||||
// 3. write headers
|
||||
// <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) /
|
||||
sizeof(uint8_t));
|
||||
if (BLOCK_SIZE < 1) {
|
||||
printf("The block size " INT2STR(BLOCK_SIZE_BYTES) " is too small, "
|
||||
"should be at least %" PRIuMAX " bytes\n", MINIMUM_BLOCK_SIZE);
|
||||
# if !SILENT_MODE
|
||||
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;
|
||||
}
|
||||
|
||||
@ -234,22 +281,23 @@ main(void)
|
||||
size_t calc_block_size = BLOCK_SIZE;
|
||||
ALLOCATE_MEMORY(calc_block_size)
|
||||
#else
|
||||
ALLOCATE_MEMORY(PRODUCT)
|
||||
ALLOCATE_MEMORY(GEN_LENGTH)
|
||||
#endif
|
||||
|
||||
#if SEQUENTIAL_MODE
|
||||
const uintmax_t gen_length_minus_1 = GEN_LENGTH - 1,
|
||||
bit_depth_limiter = BIT_DEPTH_LIMITER
|
||||
const uintmax_t bit_depth_limiter = BIT_DEPTH_LIMITER
|
||||
#if FP_RETURN_TYPE
|
||||
+ 1
|
||||
#endif
|
||||
#if BIT_DEPTH < 8
|
||||
,
|
||||
bit_depth_stretch = ((float) BIT_DEPTH) / 8.L
|
||||
+ 1
|
||||
#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++) {
|
||||
if ((time + BLOCK_SIZE) >= GEN_LENGTH)
|
||||
calc_block_size = GEN_LENGTH - time;
|
||||
@ -257,62 +305,64 @@ main(void)
|
||||
|
||||
// * bytebeat generating loop
|
||||
#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++) {
|
||||
#else
|
||||
for (size_t time = 0; time < GEN_LENGTH; time++) {
|
||||
for (size_t time = INITIAL_TIME; time < LOOP_END; time++) {
|
||||
#endif
|
||||
// 1. generate audio data
|
||||
#if FP_RETURN_TYPE
|
||||
float bytebeat_res =
|
||||
floorf(bytebeat(floorf((float) time)));
|
||||
bb_fp_return_t bytebeat_res =
|
||||
floor(bytebeat(floor((bb_counter_t) time)));
|
||||
#elif IS_SIGNED
|
||||
intmax_t bytebeat_res =
|
||||
(intmax_t) bytebeat(floorf((float) time));
|
||||
(intmax_t) bytebeat(floor((bb_counter_t) time));
|
||||
#else
|
||||
uintmax_t bytebeat_res =
|
||||
(uintmax_t) bytebeat(floorf((float) time));
|
||||
(uintmax_t) bytebeat(floor((bb_counter_t) time));
|
||||
#endif
|
||||
|
||||
// 2. if signed, then wrap up to unsigned
|
||||
#if IS_SIGNED
|
||||
// 2. if signed and bit depth <= 8, then wrap up to unsigned
|
||||
#if IS_SIGNED && (BIT_DEPTH <= 8)
|
||||
bytebeat_res = signed_to_unsigned(bytebeat_res);
|
||||
#endif
|
||||
|
||||
// 3. convert audio data to sample
|
||||
SAMPLE_TYPE sample_res = (SAMPLE_TYPE)
|
||||
#if FP_RETURN_TYPE
|
||||
fmodl(bytebeat_res, bit_depth_limiter);
|
||||
fmod(bytebeat_res, BIT_DEPTH_LIMITER);
|
||||
#else
|
||||
((uintmax_t) bytebeat_res & bit_depth_limiter);
|
||||
((uintmax_t) bytebeat_res & BIT_DEPTH_LIMITER);
|
||||
#endif
|
||||
|
||||
// 4. if bit depth is less than 8, stretch it
|
||||
#if BIT_DEPTH < 8
|
||||
// 4. if bit depth is less than BITS_PER_BYTE, stretch it
|
||||
#if BIT_DEPTH < BITS_PER_BYTE
|
||||
sample_res = (SAMPLE_TYPE)
|
||||
((float) sample_res * bit_depth_stretch);
|
||||
#endif
|
||||
|
||||
// 5. save sample into buffer
|
||||
buffer[
|
||||
#if SEQUENTIAL_MODE
|
||||
buffer[idx] = sample_res;
|
||||
idx
|
||||
#else
|
||||
buffer[time] = sample_res;
|
||||
time
|
||||
#endif
|
||||
] = sample_res;
|
||||
|
||||
// 6. log
|
||||
#if VERBOSE_MODE
|
||||
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(
|
||||
"%sremaining samples = %18" PRIuMAX " (%3.2Lf%% done)"
|
||||
"%sremaining samples = %18" PRIuMAX " (%6.2Lf%% done)"
|
||||
#if SEQUENTIAL_MODE
|
||||
" (part %" PRIuMAX "/%" PRIuMAX ")"
|
||||
#endif
|
||||
,
|
||||
_ANSI_CLEAR_STRING,
|
||||
gen_length_minus_1 - time,
|
||||
((float) time * 100) / (float) GEN_LENGTH
|
||||
LOOP_END_MINUS_1 - time,
|
||||
((long double) time * 100) / (long double) LOOP_END_MINUS_1
|
||||
#if SEQUENTIAL_MODE
|
||||
, (uintmax_t) seq + 1, (uintmax_t) MAX
|
||||
#endif
|
||||
@ -327,13 +377,13 @@ main(void)
|
||||
|
||||
// 5. log
|
||||
#if !(SEQUENTIAL_MODE && VERBOSE_MODE)
|
||||
#if SEQUENTIAL_MODE
|
||||
# if SEQUENTIAL_MODE
|
||||
if (seq == 0)
|
||||
#endif
|
||||
# endif
|
||||
puts(
|
||||
#if !SEQUENTIAL_MODE
|
||||
# if !SEQUENTIAL_MODE
|
||||
"\n"
|
||||
#endif
|
||||
# endif
|
||||
"Writing out file " OUTPUT_FILE "...");
|
||||
#endif
|
||||
fflush(stdout);
|
||||
@ -344,7 +394,7 @@ main(void)
|
||||
#if SEQUENTIAL_MODE
|
||||
calc_block_size,
|
||||
#else
|
||||
PRODUCT,
|
||||
GEN_LENGTH,
|
||||
#endif
|
||||
output_file);
|
||||
#if SEQUENTIAL_MODE
|
||||
@ -355,6 +405,45 @@ main(void)
|
||||
}
|
||||
#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(buffer);
|
||||
|
||||
@ -364,11 +453,13 @@ main(void)
|
||||
// * end of program
|
||||
#if !SILENT_MODE
|
||||
puts(
|
||||
#if SEQUENTIAL_MODE && VERBOSE_MODE
|
||||
# if SEQUENTIAL_MODE && VERBOSE_MODE && REPEAT_TIMES == 0
|
||||
"\n"
|
||||
#endif
|
||||
# endif
|
||||
"Done!");
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* _ERROR */
|
||||
|
Loading…
Reference in New Issue
Block a user