Last active
September 3, 2026 07:05
-
-
Save anytizer/822339e1391f16861e2d002ee79d0ac7 to your computer and use it in GitHub Desktop.
Sound categorizer for anticipated strengths in beats
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This file is in response to one of the LMMS Discussion thread | |
| // https://github.com/LMMS/lmms/discussions/7653#discussioncomment-18167418 | |
| // A temporary attempt to auto classify sound for calculating beat strenghts. | |
| QList<Classifications> classifications = {}; | |
| classifications.append(Classifications(Instrument::Unknown, "Unknown", {}, dw->unknown)); // Keep on top | |
| classifications.append(Classifications( | |
| Instrument::Kick, | |
| "Kick Bass Drum", | |
| { | |
| "kick", "bass", "bd", "drum", | |
| "sub", "boom", "thump", "muffle", | |
| // maadal | |
| "dhing", "dhaang", "dhang", "ghan", "ghin", | |
| // tabla | |
| "ghe", | |
| }, dw->kick)); | |
| classifications.append(Classifications( | |
| Instrument::Snare, | |
| "Snare", | |
| { | |
| "snare", "snr", "snap", "crack", | |
| "pop", "brush", "sidestick", | |
| "clap", | |
| }, dw->snare)); | |
| classifications.append(Classifications( | |
| Instrument::HiHat, | |
| "Hi-Hat", | |
| { | |
| "hat", "hihat", "hh", | |
| "open", "closed", "pedal", | |
| "splash", | |
| // bark, bottle, khat, phat | |
| }, dw->hihat)); | |
| classifications.append(Classifications( | |
| Instrument::Tom, | |
| "Tom", | |
| { | |
| "tom", // "tomtom", | |
| //"high tom", "mid tom", "low tom", "floor tom", | |
| "floor", "rack", | |
| // cowbell, conga, clave, rim, shot | |
| }, dw->tom)); | |
| classifications.append(Classifications( | |
| Instrument::Click, | |
| "Click", | |
| { | |
| "click", "tick", "rim", | |
| "rimshot", "shot", "stick", "dry", | |
| }, dw->click)); | |
| classifications.append(Classifications( | |
| Instrument::Cymbal, | |
| "Cymbal", | |
| { | |
| "cymbal", "crash", "ride", | |
| "china", "splash", | |
| "gong", "sizzle", | |
| "choke", "swell" | |
| }, dw->cymbal)); | |
| classifications.append(Classifications( | |
| Instrument::Percussion, | |
| "Percussion", | |
| { | |
| "bongo", | |
| "cowbell", "clave", "conga", | |
| "tambourine", "timbale", | |
| "cabasa", | |
| "maraca", | |
| "triangle", | |
| "wood", "block", | |
| "bell", | |
| "guiro", | |
| "jingle", | |
| "shaker", | |
| }, dw->percussion)); | |
| classifications.append(Classifications( | |
| Instrument::Others, | |
| "Others", | |
| { | |
| //"bell", // matched with cowbell :-( | |
| // "whistle", | |
| "fx", "effect", "noise", "misc" | |
| }, dw->others)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This file is in response to one of the LMMS Discussion thread | |
| // https://github.com/LMMS/lmms/discussions/7653#discussioncomment-18167418 | |
| // raw calculations of drum weights (beats placement) | |
| #pragma once | |
| #include <random> | |
| #include <cmath> | |
| #include <algorithm> | |
| #include <QList> | |
| #include <QDebug> | |
| #include <QRandomGenerator> | |
| namespace lmms | |
| { | |
| class DrumWeights | |
| { | |
| public: | |
| QList<int> unknown = {}; // all other unclassifieds | |
| QList<int> kick = {}; // kick, bass, drum | |
| QList<int> snare = {}; | |
| QList<int> hihat = {}; | |
| QList<int> tom = {}; | |
| QList<int> click = {}; | |
| QList<int> cymbal = {}; | |
| QList<int> percussion = {}; | |
| QList<int> others = {}; // sounds explicitly classified as others | |
| DrumWeights(int numerator, int denominator, int tempo, int TOTAL_STEPS) | |
| { | |
| //this->populateUnknowns(TOTAL_STEPS, TOTAL_STEPS/4); // 2, 4, 8, 16 | |
| this->populateUnknowns(TOTAL_STEPS, 4); | |
| this->populateProbabilities(numerator, denominator, tempo, TOTAL_STEPS); | |
| } | |
| private: | |
| void populateUnknowns(int TOTAL_STEPS, int howMany) | |
| { | |
| this->unknown.clear(); | |
| QList<int> randomIndexes = randomsPicks(TOTAL_STEPS, howMany); | |
| for (int step=0; step<TOTAL_STEPS; ++step) | |
| { | |
| int r_unknown = 0.0f; | |
| if(randomIndexes.contains(step)) r_unknown = 40.0f; | |
| r_unknown = ClampProbability(r_unknown, 0); | |
| this->unknown.append(r_unknown); | |
| } | |
| qDebug() << "Unknowns Plan" << this->unknown; | |
| } | |
| enum Speeds | |
| { | |
| Slower, | |
| Slow, | |
| Medium, | |
| Fast, | |
| Faster | |
| }; | |
| void populateProbabilities(int numerator, int denominator, int tempo, int TOTAL_STEPS) | |
| { | |
| // speeds calculator | |
| // // const float secondsPerQuarter = 60.0f / tempo; | |
| // //const float stepSeconds = (60.0f / tempo) * (4.0f / denominator) / 4.0f; // working!! keep! | |
| // const float stepSeconds = (60.0f / tempo) * denominator; | |
| // Speeds speed = Speeds::Medium; | |
| // if (stepSeconds >= 0.200f) speed = Speeds::Slower; | |
| // else if (stepSeconds >= 0.140f) speed = Speeds::Slow; | |
| // else if (stepSeconds >= 0.090f) speed = Speeds::Medium; | |
| // else if (stepSeconds >= 0.060f) speed = Speeds::Fast; | |
| // else speed = Speeds::Faster; | |
| //this->unknown.clear(); | |
| this->kick.clear(); | |
| this->snare.clear(); | |
| this->hihat.clear(); | |
| this->tom.clear(); | |
| this->click.clear(); | |
| this->cymbal.clear(); | |
| this->percussion.clear(); | |
| this->others.clear(); | |
| //float density = Density(tempo); // @todo make use of TOTAL_STEPS as well. | |
| float density = CalculateDensity(tempo, TOTAL_STEPS); // , 16.0f); // 500f | |
| // populate eveything else | |
| for (int step=0; step<TOTAL_STEPS; ++step) | |
| { | |
| int strength = BeatStrength(step, numerator); | |
| // Generate random values per step | |
| //int r_unknown = UnknownRule(step, density); | |
| int r_kick = KickRule(step, density, strength); | |
| int r_snare = SnareRule(step, density, numerator); | |
| int r_hihat = HiHatRule(step, density); | |
| int r_tom = TomRule(step, density); | |
| int r_click = ClickRule(step, density); | |
| int r_cymbal = CymbalRule(step, density); | |
| int r_percussion = PercussionRule(step, density); | |
| int r_others = OtherRule(step, density); | |
| //this->unknown.append(r_unknown); | |
| this->kick.append(r_kick); | |
| this->snare.append(r_snare); | |
| this->hihat.append(r_hihat); | |
| this->tom.append(r_tom); | |
| this->click.append(r_click); | |
| this->cymbal.append(r_cymbal); | |
| this->percussion.append(r_percussion); | |
| this->others.append(r_others); | |
| } | |
| } | |
| /** | |
| PLAN : 4 / 4 @ 140 = 32 | |
| unknwn: 83 0 90 0 0 0 72 0 0 0 0 0 0 0 0 51 | |
| 0 0 0 0 0 0 0 0 40 39 0 0 1 65 0 0 | |
| kick : 100 25 43 33 90 9 27 17 98 17 35 25 90 9 27 17 | |
| 26 0 0 0 26 0 0 0 26 0 0 0 26 0 0 0 | |
| snare : 0 4 6 9 100 4 6 9 0 4 6 9 100 4 6 9 | |
| 0 4 6 9 0 4 6 9 0 4 6 9 0 4 6 9 | |
| hihat : 95 45 63 45 95 45 63 45 95 45 63 45 95 45 63 45 | |
| 95 45 63 45 95 45 63 45 95 45 63 45 95 45 63 45 | |
| tom : 52 5 14 5 30 5 14 5 52 5 14 5 30 5 14 5 | |
| 52 5 14 5 30 5 14 5 52 5 14 5 30 5 14 5 | |
| click : 4 4 21 4 37 4 21 4 4 4 21 4 37 4 21 4 | |
| 4 4 21 4 37 4 21 4 4 4 21 4 37 4 21 4 | |
| cymbal: 69 2 2 2 11 2 2 2 28 2 2 2 11 2 2 2 | |
| 69 2 2 2 11 2 2 2 28 2 2 2 11 2 2 2 | |
| percsn: 29 28 52 28 45 28 52 28 16 28 52 28 45 28 52 28 | |
| 29 28 52 28 45 28 52 28 16 28 52 28 45 28 52 28 | |
| others: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | |
| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | |
| */ | |
| QList<int> randomsPicks(int totalIterations, int maxRandomPicks) | |
| { | |
| // 1. Generate a list of all available indexes (0 to 15) | |
| QList<int> allIndexes; | |
| for (int i = 0; i < totalIterations; ++i) { | |
| allIndexes.append(i); | |
| } | |
| // 2. Shuffle the indexes randomly | |
| // QRandomGenerator::global() provides secure, modern random numbers in Qt 5/6 | |
| std::random_device rd; | |
| std::mt19937 generator(rd()); | |
| std::shuffle(allIndexes.begin(), allIndexes.end(), generator); | |
| // std::random_shuffle(allIndexes.begin(), allIndexes.end(), [](int i) { | |
| // return QRandomGenerator::global()->bounded(i); | |
| // }); | |
| // 3. Pick the first N indexes (up to maxRandomPicks) | |
| QList<int> selectedIndexes; | |
| int picks = qMin(maxRandomPicks, totalIterations); | |
| for (int i = 0; i < picks; ++i) { | |
| selectedIndexes.append(allIndexes[i]); | |
| } | |
| // 4. Overhead for screen print only | |
| std::sort(selectedIndexes.begin(), selectedIndexes.end()); | |
| qDebug() << "Selected Random Indexes:" << selectedIndexes; | |
| return selectedIndexes; | |
| } | |
| /** | |
| 1. Beat strength | |
| Assign every 16th note a strength value. | |
| For 4/4 (16 steps): | |
| Step: 1 2 3 4 | 5 6 7 8 | 9 10 11 12 |13 14 15 16 | |
| Strength: 10 2 4 3 | 8 2 4 3 | 9 2 4 3 | 8 2 4 3 | |
| For 3/4 (12 steps): | |
| 10 2 4 3 | 8 2 4 3 | 8 2 4 3 | |
| For 2/4 (8 steps): | |
| 10 2 4 3 | 8 2 4 3 | |
| */ | |
| int BeatStrength(int step, int numerator) | |
| { | |
| int beat = step / 4; | |
| int pos = step % 4; | |
| static const int beatAccent4[] = {10,8,9,8}; | |
| static const int beatAccent3[] = {10,8,8}; | |
| static const int beatAccent2[] = {10,8}; | |
| int accent; | |
| switch (numerator) | |
| { | |
| case 2: accent = beatAccent2[beat]; break; | |
| case 3: accent = beatAccent3[beat]; break; | |
| default: accent = beatAccent4[beat]; | |
| } | |
| static const int subdivision[] = {0,-8,-6,-7}; | |
| return accent + subdivision[pos]; | |
| } | |
| /** | |
| * Tempo → Density | |
| * Instead of five speeds (tables), compute a density factor. | |
| */ | |
| float Density(float bpm) | |
| { | |
| if (bpm < 70) return 0.25f; | |
| if (bpm < 100) return 0.45f; | |
| if (bpm < 140) return 0.65f; | |
| if (bpm < 180) return 0.85f; | |
| return 1.00f; | |
| } | |
| /** | |
| * Set maxNotesBaseline to whatever numerical target defines a "full" or standard arrangement in application. | |
| * For example: | |
| * If a standard 3-minute procedural track usually contains around 500 notes, set it to 500f. | |
| * If you are generating short loops or micro-rhythms with only 64 notes, set it to 64f. | |
| */ | |
| float CalculateDensity(float bpm, int TOTAL_STEPS=16) | |
| { | |
| int maxNotesBaseline = 64; // 64 | 16 | TOTAL_STEPS | |
| // 1. Base tempo scaling (faster tempos naturally reduce per-beat multiplier to avoid clutter) | |
| float tempoFactor; | |
| if (bpm < 70) tempoFactor = 0.25f; | |
| else if (bpm < 100) tempoFactor = 0.45f; | |
| else if (bpm < 140) tempoFactor = 0.65f; | |
| else if (bpm < 180) tempoFactor = 0.85f; | |
| else tempoFactor = 1.00f; | |
| // 2. Note load scaling (ratio of desired total notes against a standard baseline) | |
| // Clamp the ratio so extreme note counts don't break your math | |
| float noteFactor = std::clamp((float)TOTAL_STEPS / maxNotesBaseline, 0.1f, 2.0f); | |
| // 3. Combine factors (multiplicative or weighted blend) | |
| float finalDensity = tempoFactor * noteFactor; | |
| // Clamp final output to standard normalized range [0.0f, 1.0f] | |
| //return Mathf.Clamp01(finalDensity); | |
| return std::clamp(finalDensity, 0.00f, 1.00f); | |
| } | |
| /** | |
| Typical 4/4 result at 120 BPM: | |
| 100 12 28 16 | |
| 84 10 25 18 | |
| 92 12 30 20 | |
| 84 15 30 18 | |
| // 1. Bass Drum: Strong hits on main beats (0, 4, 8, 12), occasional syncopation | |
| // from rule | |
| // bool isMainBeat = (step % 4 == 0); | |
| // double kickChance = isMainBeat ? 0.85 : 0.15; | |
| // add_note = dice < kickChance; | |
| */ | |
| /** | |
| QVector<int> indexes; | |
| for (int i = 0; i < 16; ++i) | |
| indexes.append(i); | |
| std::shuffle(indexes.begin(), indexes.end(), *QRandomGenerator::global()); | |
| QVector<int> selected(indexes.begin(), indexes.begin() + 4); | |
| for (int index : selected) | |
| { | |
| qDebug() << "Picked:" << index; | |
| } | |
| */ | |
| int KickRule(int step, float density, int strength) | |
| { | |
| float p = 0.0f; | |
| // Beat strength | |
| p += strength * 8.0f; | |
| // More fills at higher tempos | |
| p += density * 15.0f; | |
| switch (step % 4) | |
| { | |
| case 0: // Quarter note | |
| p += 20.0f; | |
| break; | |
| case 2: // Eighth | |
| p += density * 12.0f; | |
| break; | |
| default: // 16ths | |
| p += density * 6.0f; | |
| break; | |
| } | |
| // Downbeat always wins | |
| if (step == 0) | |
| p = 100; | |
| return ClampProbability(p, 0); | |
| } | |
| /** | |
| Typical 4/4: | |
| 0 5 10 15 | |
| 100 8 12 18 | |
| 0 5 10 15 | |
| 100 8 12 18 | |
| // 2. Snare: Heavy backbeats on steps 4 and 12 (beats 2 and 4) | |
| // bool isBackbeat = (step == 4 || step == 12); | |
| // double snareChance = isBackbeat ? 0.95 : 0.03; | |
| // add_note = dice < snareChance; | |
| */ | |
| int SnareRule(int step, | |
| float density, | |
| int numerator | |
| ) | |
| { | |
| // Main backbeats | |
| if (numerator == 4) | |
| { | |
| if (step == 4 || step == 12) | |
| return 100; | |
| } | |
| else if (numerator == 3) | |
| { | |
| if (step == 4) | |
| return 100; | |
| if (step == 8) | |
| return 90; | |
| } | |
| else if (numerator == 2) | |
| { | |
| if (step == 4) | |
| return 100; | |
| } | |
| // Ghost notes | |
| float p = 0.0f; | |
| switch (step % 4) | |
| { | |
| case 1: p = density * 10; break; | |
| case 2: p = density * 15; break; | |
| case 3: p = density * 20; break; | |
| } | |
| return ClampProbability(p, 0); | |
| } | |
| /** | |
| * Hi-hat density increases almost linearly with tempo. | |
| Slow (density = 0.25) | |
| 95 33 54 32 | |
| 95 33 54 32 | |
| 95 33 54 32 | |
| 95 33 54 32 | |
| Medium (density = 0.65) | |
| 95 60 76 61 | |
| 95 60 76 61 | |
| 95 60 76 61 | |
| 95 60 76 61 | |
| Very Fast (density = 1.0) | |
| 95 85 95 85 | |
| 95 85 95 85 | |
| 95 85 95 85 | |
| 95 85 95 85 | |
| // 3. Hi-Hat: High density driving the 16th-note subdivision | |
| // add_note = dice < 0.65; | |
| */ | |
| int HiHatRule(int step, float density) | |
| { | |
| if (step % 4 == 0) | |
| { | |
| // Quarter notes | |
| return 95; | |
| } | |
| if (step % 2 == 0) | |
| { | |
| // Eighths | |
| return ClampProbability(40 + density * 55, 0); | |
| } | |
| // 16ths | |
| return ClampProbability(15 + density * 70, 0); | |
| } | |
| /** | |
| Density 0.2 | |
| T--- ---- T--- ---- | |
| Density 0.5 | |
| T--- --T- T--- -T-- | |
| Density 1.0 | |
| T-T- T-T- T-T- -TT- | |
| | Density | Result | | |
| | ------: | ------------------------------------------------------------ | | |
| | **0.0** | Mostly only strong beat accents. | | |
| | **0.5** | Occasional tom hits on quarter and some eighth notes. | | |
| | **1.0** | Fairly active tom groove with rare 16th-note embellishments. | | |
| // 5. Tom: Accent/fill instrument, more likely toward the end of the bar (steps 12 to 15) | |
| // bool isFillArea = (step >= 12); | |
| // int tomChance = isFillArea ? 35 : 5; | |
| // add_note = dice < tomChance; | |
| */ | |
| int TomRule(int step, float density) | |
| { | |
| if (step % 8 == 0) | |
| { | |
| // Strong accents (beats 1 and 3) | |
| return ClampProbability(35 + density * 40, 0); | |
| } | |
| if (step % 4 == 0) | |
| { | |
| // Secondary accents (beats 2 and 4) | |
| return ClampProbability(15 + density * 35, 0); | |
| } | |
| if (step % 2 == 0) | |
| { | |
| // Occasional eighth-note toms | |
| return ClampProbability(5 + density * 20, 0); | |
| } | |
| // Rare 16th-note hits, mostly at high density | |
| return ClampProbability(density * 12, 0); | |
| } | |
| /** | |
| 0.0 → Almost silent. | |
| 0.5 → Occasional syncopated clicks. | |
| 1.0 → Frequent off-beat accents. | |
| */ | |
| int ClickRule(int step, float density) | |
| { | |
| if (step % 8 == 4) | |
| { | |
| // Off-beat accent | |
| return ClampProbability(20 + density * 40, 0); | |
| } | |
| if (step % 4 == 2) | |
| { | |
| // Secondary syncopation | |
| return ClampProbability(10 + density * 25, 0); | |
| } | |
| // Rare ghost clicks | |
| return ClampProbability(density * 10, 0); | |
| } | |
| /** | |
| 0.0 → Sparse eighth notes. | |
| 0.5 → Steady eighth-note groove with occasional 16ths. | |
| 1.0 → Nearly continuous 16th-note shaker. | |
| | Density | Click | Shaker | | |
| | ------: | :----------------: | :----------------: | | |
| | **0.2** | `----C-------C---` | `S-S-S-S-S-S-S-S-` | | |
| | **0.5** | `--C-----C---C---` | `SS-SS-SS-SS-SS-S` | | |
| | **1.0** | `-C-C-C--CC-C-C-C` | `SSSSSSSSSSSSSSSS` | | |
| */ | |
| // int ShakerRule(int step, float density) | |
| // { | |
| // if (step % 2 == 0) | |
| // { | |
| // // Eighth-note pulse | |
| // return ClampProbability(30 + density * 60, 0); | |
| // } | |
| // // 16th-note embellishments | |
| // return ClampProbability(10 + density * 50, 0); | |
| // } | |
| /** | |
| Density 0.2 | |
| C--- ---- ---- ---- | |
| Density 0.5 | |
| C--- ---- C--- ---- | |
| Density 1.0 | |
| C--- C--- C--- ---- | |
| | Density | Typical Pattern | | |
| | ------: | --------------------------------------------------------- | | |
| | **0.0** | Crash mainly at the start of the bar. | | |
| | **0.5** | Start-of-bar crash with occasional mid-bar cymbal. | | |
| | **1.0** | Frequent crashes/rides on strong beats, but still sparse. | | |
| // 6. Cymbal: Strong accent on the very first downbeat (step 0) | |
| // bool isDownbeat = (step == 0); | |
| // int cymbalChance = isDownbeat ? 90 : 2; | |
| // add_note = dice < cymbalChance; | |
| // Cymbal Breakdown by Subdivision | |
| // Quarter Notes: 4 repetitions per measure (hits on counts 1, 2, 3, 4). | |
| // Eighth Notes: 8 repetitions per measure (hits on counts 1 & 2 & 3 & 4 &). | |
| // Sixteenth Notes: 16 repetitions per measure (hits on 1 e & a 2 e & | |
| */ | |
| int CymbalRule(int step, float density) | |
| { | |
| if (step % 16 == 0) | |
| { | |
| // Phrase/bar start (strong crash) | |
| return ClampProbability(50 + density * 45, 0); | |
| } | |
| if (step % 8 == 0) | |
| { | |
| // Mid-bar accent or occasional ride | |
| return ClampProbability(15 + density * 30, 0); | |
| } | |
| if (step % 4 == 0) | |
| { | |
| // Rare quarter-note accents | |
| return ClampProbability(5 + density * 15, 0); | |
| } | |
| // Almost never elsewhere | |
| return ClampProbability(density * 5, 0); | |
| } | |
| /** | |
| Behavior | |
| Steps 0, 16, ...: Occasional phrase accents. | |
| Steps 4, 12, ...: Strong chance for percussion fills. | |
| Steps 2, 6, 10, 14, ...: Good probability for syncopated hits. | |
| Odd sixteenths: Light ghost percussion. | |
| Remaining positions: Low probability. | |
| This produces patterns like: | |
| Kick: X---X---X---X--- | |
| Snare: ----X-------X--- | |
| Hi-Hat: x-x-x-x-x-x-x-x- | |
| Percussion: --o-o---o-o--o-- | |
| Cymbal: C-------r------- | |
| */ | |
| int PercussionRule(int step, float density) | |
| { | |
| if (step % 16 == 0) | |
| { | |
| // Phrase start (conga, cowbell, etc.) | |
| return ClampProbability(20 + density * 20, 0); | |
| } | |
| if (step % 8 == 4) | |
| { | |
| // Backbeat embellishment | |
| return ClampProbability(30 + density * 35, 0); | |
| } | |
| if (step % 4 == 2) | |
| { | |
| // Off-beat groove | |
| return ClampProbability(35 + density * 40, 0); | |
| } | |
| if (step % 2 == 1) | |
| { | |
| // Sixteenth-note syncopation | |
| return ClampProbability(15 + density * 30, 0); | |
| } | |
| // Quarter-note positions | |
| return ClampProbability(10 + density * 15, 0); | |
| } | |
| /** | |
| * For sounds classified as OTHERS | |
| */ | |
| int OtherRule(int step, float density) | |
| { | |
| float p = 0.00f; | |
| return ClampProbability(p, 0); | |
| } | |
| /** | |
| Kick → Foundation | |
| Snare → Backbeat | |
| Hi-Hat → Primary groove | |
| Tom → Fills/accents | |
| Cymbal → Crashes and transitions | |
| Click → Sparse syncopation | |
| Shaker → Continuous high-frequency texture | |
| */ | |
| // int Humanize(int p) | |
| // { | |
| // p += Random(-5, 5); // ±5% | |
| // return std::clamp(p, 0, 100); | |
| // } | |
| // inline int ClampProbability(int probability) | |
| // { | |
| // return std::clamp( | |
| // static_cast<int>(std::round(probability)), | |
| // 0, | |
| // 100 | |
| // ); | |
| // } | |
| // drum probabilities: combine clamping and humanization into one function: | |
| inline int ClampProbability(float probability, int variation = 0) | |
| { | |
| probability += static_cast<float>(Random(-variation, +variation)); | |
| if (probability < 0.0f) probability = 0.0f; | |
| if (probability > 100.0f) probability = 100.0f; | |
| return static_cast<int>(std::round(probability)); | |
| } | |
| float Random(const int minimum, const int maximum) | |
| { | |
| static std::random_device rd; | |
| static std::mt19937 generator(rd()); | |
| std::uniform_real_distribution<float> distribution( | |
| static_cast<float>(minimum), | |
| static_cast<float>(maximum) | |
| ); | |
| return distribution(generator); | |
| } | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment