Skip to content

Commit

Permalink
feat: allow users to define their own random number generator
Browse files Browse the repository at this point in the history
  • Loading branch information
lmichaelis committed May 2, 2024
1 parent 6125561 commit 9ce1073
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ list(APPEND _DM_SOURCE
src/Message.c
src/Performance.c
src/Riff.c
src/Rng.c
src/Segment.c
src/Style.c
src/Synth.c
Expand Down
28 changes: 28 additions & 0 deletions include/dmusic.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,34 @@ typedef void DmMemoryFree(void* ctx, void* ptr);
/// \see DmMemoryFree De-allocation function definition.
DMAPI DmResult Dm_setHeapAllocator(DmMemoryAlloc* alloc, DmMemoryFree* free, void* ctx);

/// \brief A `rand_r`-like random number generation function.
///
/// Generates a random number between 0 and UINT32_MAX. Functions implementing this interface must be
/// thread-safe. The function is not required to produce different numbers upon invocation and it does
/// not need to be cryptographically safe. Calling a function implementing this interface should be
/// inexpensive.
///
/// \param ctx[in] An arbitrary pointer provided when calling #Dm_setRandomNumberGenerator.
/// \return A random number between 0 and UINT32_MAX.
/// \see Dm_setRandomNumberGenerator
typedef uint32_t DmRng(void* ctx);

/// \brief Set the random number generator to use internally.
///
/// The given random number generator is sampled every time the library requires a random number. This includes
/// but is not limited to:
///
/// * Selecting the next pattern to be played,
/// * selecting the next note/curve variation and
/// * applying random note offsets
///
/// \param rng[in] A pointer to a function to use as a random number generator or `NULL`
/// to reset to the default random number generator.
/// \param ctx[in] An arbitrary pointer passed to \p rng on every invocation.
///
/// \see DmRng Requirements for the random number generator
DMAPI void Dm_setRandomNumberGenerator(DmRng* rng, void* ctx);

/// \brief The set of message levels supported by the logging facilities.
typedef enum DmLogLevel {
/// \brief The log message indicates a fatal error.
Expand Down
4 changes: 2 additions & 2 deletions src/Common.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ float clamp_f32(float val, float min, float max) {
}

int32_t Dm_randRange(int32_t range) {
int32_t rnd = rand() % range;
return range - (rnd / 2);
uint32_t rnd = Dm_rand() % range;
return range - (int32_t) (rnd / 2);
}

DmCommandType Dm_embellishmentToCommand(DmEmbellishmentType embellishment) {
Expand Down
4 changes: 2 additions & 2 deletions src/Performance.c
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ static DmResult DmPattern_generateMessages(DmPattern* slf,
variation[pref->variation_lock_id] = seq;
break;
case DmVariation_RANDOM:
variation[pref->variation_lock_id] = rand();
variation[pref->variation_lock_id] = Dm_rand();
break;
case DmVariation_RANDOM_START:
// TODO(lmichaelis): Implement this correctly. To do that, we need to store the previous
Expand All @@ -578,7 +578,7 @@ static DmResult DmPattern_generateMessages(DmPattern* slf,
case DmVariation_NO_REPEAT:
// TODO(lmichaelis): Implement this correctly. To do that, we need to store the previous
// variation id for each pattern somewhere and compare it to the next value
variation[pref->variation_lock_id] = rand();
variation[pref->variation_lock_id] = Dm_rand();
break;
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/Rng.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright © 2024. GothicKit Contributors
// SPDX-License-Identifier: MIT-Modern-Variant
#include "_Internal.h"

#include <stdlib.h>

static uint32_t DmInt_defaultRng(void* ctx);

static DmRng* DmGlob_rngCallback = DmInt_defaultRng;
static void* DmGlob_rngContext = NULL;

uint32_t Dm_rand(void) {
return DmGlob_rngCallback(DmGlob_rngContext);
}

void Dm_setRandomNumberGenerator(DmRng* rng, void* ctx) {
if (rng == NULL) {
DmGlob_rngCallback = DmInt_defaultRng;
DmGlob_rngContext = NULL;
return;
}

DmGlob_rngCallback = rng;
DmGlob_rngContext = ctx;
}

static uint32_t DmInt_defaultRng(void* ctx) {
(void) ctx;
return rand();
}
2 changes: 1 addition & 1 deletion src/Style.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ DmPattern* DmStyle_getRandomPattern(DmStyle* slf, uint32_t groove, DmCommandType
// have some way of defining how to select the pattern if more than 1 choice is available
// but I couldn't find it.

int32_t index = rand() % slf->patterns.length;
int64_t index = Dm_rand() % (uint32_t) slf->patterns.length;
do {
for (size_t i = 0; i < slf->patterns.length; ++i) {
DmPattern* pttn = &slf->patterns.data[i];
Expand Down
1 change: 1 addition & 0 deletions src/_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ struct DmPerformance {
DMINT void* Dm_alloc(size_t len);
DMINT void Dm_free(void* ptr);
DMINT void Dm_report(DmLogLevel lvl, char const* fmt, ...);
DMINT uint32_t Dm_rand(void);

DMINT size_t max_usize(size_t a, size_t b);
DMINT int32_t max_s32(int32_t a, int32_t b);
Expand Down

0 comments on commit 9ce1073

Please sign in to comment.