diff --git a/README.md b/README.md index 9538ab7..15ae8c0 100644 --- a/README.md +++ b/README.md @@ -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)). diff --git a/bytebeat_compiler.py b/bytebeat_compiler.py index 3823f2d..ca2fabb 100644 --- a/bytebeat_compiler.py +++ b/bytebeat_compiler.py @@ -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, diff --git a/include/fwrite_le.h b/include/fwrite_le.h index 69e52b1..58d6ef6 100644 --- a/include/fwrite_le.h +++ b/include/fwrite_le.h @@ -1,24 +1,23 @@ +#ifndef _FWRITE_LE_H +#define _FWRITE_LE_H + #include #include #include #include -#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, diff --git a/samples/beeper.c b/samples/beeper.c new file mode 100644 index 0000000..54034a7 --- /dev/null +++ b/samples/beeper.c @@ -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; 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..9c4ba5c --- /dev/null +++ b/samples/melody.c @@ -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; diff --git a/src/fwrite_le.c b/src/fwrite_le.c index 473124c..748de52 100644 --- a/src/fwrite_le.c +++ b/src/fwrite_le.c @@ -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); diff --git a/src/template.c b/src/template.c index 815fce8..0a2ff4d 100644 --- a/src/template.c +++ b/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 // : : @@ -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 */