diff --git a/Makefile b/Makefile index e89dbdd..a3c78e0 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,8 @@ V = 0.0 LDLIBS = -lm -HDR = err.h note.h synth.h -OBJ = err.o main.o note.o synth.o +HDR = err.h lrand48_uniform.h note.h synth.h +OBJ = err.o lrand48_uniform.o main.o note.o synth.o SRC = ${OBJ:.o=.c} DIST_FILES = COPYING Makefile ${HDR} ${SRC} diff --git a/lrand48_uniform.c b/lrand48_uniform.c new file mode 100644 index 0000000..c52259e --- /dev/null +++ b/lrand48_uniform.c @@ -0,0 +1,70 @@ +/* $OpenBSD: arc4random_uniform.c,v 1.3 2019/01/20 02:59:07 bcook Exp $ */ + +/* + * Copyright (c) 2008, Damien Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * arngment - random music generator + * + * Written in 2020 by Lucas + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include +#include + +/* + * Calculate a uniformly distributed random number less than upper_bound + * avoiding "modulo bias". + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**32 % upper_bound). This + * guarantees the selected random number will be inside + * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) + * after reduction modulo upper_bound. + */ +long +lrand48_uniform(uint32_t upper_bound) +{ + uint32_t r, min; + + if (upper_bound < 2) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + min = -upper_bound % upper_bound; + + /* + * This could theoretically loop forever but each retry has + * p > 0.5 (worst case, usually far better) of selecting a + * number inside the range we need, so it should rarely need + * to re-roll. + */ + for (;;) { + r = lrand48(); + if (r >= min) + break; + } + + return r % upper_bound; +} diff --git a/lrand48_uniform.h b/lrand48_uniform.h new file mode 100644 index 0000000..8ac2e03 --- /dev/null +++ b/lrand48_uniform.h @@ -0,0 +1,18 @@ +/* + * arngment - random music generator + * + * Written in 2020 by Lucas + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include + +long lrand48_uniform(uint32_t); diff --git a/main.c b/main.c index 91c0862..089c347 100644 --- a/main.c +++ b/main.c @@ -17,9 +17,11 @@ #include #include #include +#include #include #include "err.h" +#include "lrand48_uniform.h" #include "note.h" #include "synth.h" @@ -63,8 +65,9 @@ main(int argc, char *argv[]) if (!note_parse(&base_note, argv[0])) errx(1, "Invalid note %s", argv[0]); + srand48(time(NULL)); for (;;) { - k = arc4random_uniform(nelems(scale)); + k = lrand48_uniform(nelems(scale)); note = base_note; note.offset += scale[k]; for (i = 0; i < RATE / 2; i += 2) { @@ -77,7 +80,7 @@ main(int argc, char *argv[]) buf[i + 1] = r * INT16_MAX; } - k = arc4random_uniform(nelems(scale)); + k = lrand48_uniform(nelems(scale)); note = base_note; note.offset += scale[k]; for (; i < RATE; i += 2) {