diff --git a/bytebeat_compiler.py b/bytebeat_compiler.py index 3371a09..917d8d6 100644 --- a/bytebeat_compiler.py +++ b/bytebeat_compiler.py @@ -13,6 +13,7 @@ from shutil import which from sys import stdin, stdout from tempfile import TemporaryDirectory from typing import Dict, Union +import re import subprocess # Definitions @@ -53,6 +54,18 @@ DEFAULT_PARAMETERS = { 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): if from_env := environ.get(name): return from_env @@ -97,7 +110,7 @@ def run_command(*command: list[str], stage: str) -> None: 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") run_command( @@ -105,16 +118,16 @@ def compile_substituted_file(input_file: str, executable_file: str) -> None: *command_line_split(CFLAGS), input_file, PATHS["fwrite_le"], - "-o", executable_file, + "-o", output_file, "-I" + PATHS["include_directory"], 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: 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" C_str_repr = lambda s: '"' + s.replace("\\", "\\\\").replace(r'"', r'\"') + '"' @@ -136,6 +149,26 @@ CFLAGS = fetch("CFLAGS") 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", + "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_unavailable = lambda cmd: which(cmd) is None @@ -148,38 +181,46 @@ 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 WAVE file path (default is `output.wav`") - parser.add_argument("-r", "--sample-rate", default=8000, type=int, + help="specify output WAVE 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=Decimal, 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", "--custom-return-code", default=False, - action="store_true", - help="do not insert return statement before the code") - parser.add_argument("-u", "--mode", default="sequential", type=str, + "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", "--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 " "recommended, since the whole result would be stored in RAM)") parser.add_argument("-n", "--block-size", default=65536, type=int, @@ -203,6 +244,42 @@ if __name__ == "__main__": if not bytebeat_contents: 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 if not args.custom_return_code: # Insert `return` statement # XXX: The bytebeat code is enclosed in parentheses to allow for the @@ -213,7 +290,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;" @@ -223,7 +300,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 @@ -261,6 +339,43 @@ if __name__ == "__main__": 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 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): print(f"Compiler {CC} is not available, searching:") @@ -283,8 +398,7 @@ if __name__ == "__main__": substitute_contents = 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, @@ -293,7 +407,11 @@ if __name__ == "__main__": "faster_sample_ratio_math": args.precalculate_ratio, "fp_return_type": args.floating_point, "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), "gen_length": gen_length, "sequential_mode": args.mode == "sequential", diff --git a/samples/beeper.c b/samples/beeper.c new file mode 100644 index 0000000..72d571f --- /dev/null +++ b/samples/beeper.c @@ -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; diff --git a/samples/cykstep.c b/samples/cykstep.c new file mode 100644 index 0000000..ed3535c --- /dev/null +++ b/samples/cykstep.c @@ -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))) diff --git a/samples/melody.c b/samples/melody.c new file mode 100644 index 0000000..e136dc8 --- /dev/null +++ b/samples/melody.c @@ -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; diff --git a/samples/pwm-sierpinski.c b/samples/pwm-sierpinski.c new file mode 100644 index 0000000..b6fa0bb --- /dev/null +++ b/samples/pwm-sierpinski.c @@ -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 diff --git a/src/template.c b/src/template.c index 21bf901..17a404e 100644 --- a/src/template.c +++ b/src/template.c @@ -11,6 +11,42 @@ #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 #define ANSI_ESCAPE_CODES_SUPPORTED `ansi_escape_codes_supported` @@ -31,22 +67,42 @@ #define BIT_DEPTH `bit_depth` #define IS_SIGNED `is_signed` #define CHANNELS `channels` -#define LENGTH `length` +#define RUNNING_LENGTH `running_length` #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` @@ -57,8 +113,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) @@ -82,19 +138,19 @@ // function prototypes #if FP_RETURN_TYPE -long double +bb_fp_return_t #else SAMPLE_TYPE #endif -bytebeat(long double time); +bytebeat(bb_counter_t time); // function implementations #if FP_RETURN_TYPE -long double +bb_fp_return_t #else SAMPLE_TYPE #endif -bytebeat(long double time) +bytebeat(bb_counter_t time) { #if PRECALCULATED_RATIO `final_sample_rate_code` @@ -109,37 +165,10 @@ bytebeat(long double time) `bytebeat_contents`; } -int -main(void) -{ - // * log -> welcome -#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: "); +void +print_time(uintmax_t length) { + const uintmax_t seconds = length / SAMPLE_RATE, + samples = length % SAMPLE_RATE; if (seconds > 0) if (seconds >= 3600) @@ -158,22 +187,85 @@ main(void) if (samples > 0) 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 puts("\n"); # 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); #endif // * write WAVE headers // 0. log -#if !SILENT_MODE && 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"); @@ -181,23 +273,23 @@ main(void) } // 2. prepare variables - uint32_t buffer_size = PRODUCT, - file_length = - /* file length without "RIFF" chunk */ - 3 * 4 /* 3 strings of 4 characters: "WAVE", "fmt ", "data" */ + - 4 * 4 /* 4 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 */; + + 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) / BITS_PER_BYTE; + 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) / BITS_PER_BYTE, - bit_depth = BIT_DEPTH > BITS_PER_BYTE ? BIT_DEPTH : BITS_PER_BYTE; + block_align = (ACTUAL_BIT_DEPTH * CHANNELS) / BITS_PER_BYTE, + bit_depth = ACTUAL_BIT_DEPTH; // 3. write headers // : : @@ -236,22 +328,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 < BITS_PER_BYTE - , - bit_depth_stretch = BIT_DEPTH / BITS_PER_BYTE + + 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; @@ -259,34 +352,34 @@ 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 - long double bytebeat_res = - floor(bytebeat(floor((long double) time))); + bb_fp_return_t bytebeat_res = + floor(bytebeat(floor((bb_counter_t) time))); #elif IS_SIGNED intmax_t bytebeat_res = - (intmax_t) bytebeat(floor((long double) time)); + (intmax_t) bytebeat(floor((bb_counter_t) time)); #else uintmax_t bytebeat_res = - (uintmax_t) bytebeat(floor((long double) 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 BITS_PER_BYTE, stretch it @@ -296,26 +389,28 @@ main(void) #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)" -# if SEQUENTIAL_MODE + "%sremaining samples = %18" PRIuMAX " (%6.2Lf%% done)" +#if SEQUENTIAL_MODE " (part %" PRIuMAX "/%" PRIuMAX ")" # endif , _ANSI_CLEAR_STRING, - gen_length_minus_1 - time, - ((long double) time * 100) / (long double) GEN_LENGTH -# if SEQUENTIAL_MODE + (uintmax_t) (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 ); @@ -346,7 +441,7 @@ main(void) #if SEQUENTIAL_MODE calc_block_size, #else - PRODUCT, + GEN_LENGTH, #endif output_file); #if SEQUENTIAL_MODE @@ -357,6 +452,47 @@ 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 + 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(buffer); @@ -366,7 +502,7 @@ main(void) // * end of program #if !SILENT_MODE puts( -# if SEQUENTIAL_MODE && VERBOSE_MODE +# if SEQUENTIAL_MODE && VERBOSE_MODE && REPEAT_TIMES == 0 "\n" # endif "Done!"); @@ -374,3 +510,5 @@ main(void) return EXIT_SUCCESS; } + +#endif /* _ERROR */