Skip to content

Commit 8c0af87

Browse files
committed
seed
1 parent 329d867 commit 8c0af87

8 files changed

Lines changed: 135 additions & 9 deletions

File tree

‎docs/todo.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,18 @@ The BassGen "MIDI Follow" system (Note On response for drum triggering) and the
3434
can coexist on the same channel. However, the UI should make this clearer — consider
3535
a tooltip or panel note explaining that Follow Ch and Conductor Ch serve different
3636
purposes and can differ.
37+
38+
## Newer plugins needing evaluation
39+
40+
* ambo
41+
* arpgen
42+
* conductor
43+
* drift
44+
* harmonic-atlas
45+
* mnemosyne
46+
* mosaic
47+
* oracle
48+
* orbit
49+
* polymeter
50+
* resonance-garden
51+
* tuny-vst

‎plugins/p-mix/include/p_mix_core_types.hpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct Parameters {
1818
float fadeDurMax = 1.0f;
1919
float bias = 50.0f;
2020
float mute = 0.0f;
21+
float seed = 0.0f; // normalized [0,1]; 0 = not yet generated
2122
};
2223

2324
struct TransportSnapshot {

‎plugins/p-mix/include/p_mix_engine.hpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct AudioBlock {
1414
};
1515

1616
[[nodiscard]] Parameters clampParameters(const Parameters& raw);
17-
void activate(EngineState& state);
17+
void activate(EngineState& state, std::uint32_t seed = 0u);
1818
void processBlock(EngineState& state,
1919
const Parameters& parameters,
2020
const TransportSnapshot& transport,

‎plugins/p-mix/src/dpf/PMixPlugin.cpp‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ enum ParameterIndex : uint32_t {
1818
kParamFadeDurMax,
1919
kParamBias,
2020
kParamMute,
21+
kParamSeed,
2122
kParameterCount
2223
};
2324

25+
[[nodiscard]] uint32_t seedToUint(float seed) {
26+
if (seed <= 0.0f) return 0u;
27+
return static_cast<uint32_t>(seed * static_cast<float>(0xFFFFFFFFu));
28+
}
29+
2430
enum StateIndex : uint32_t {
2531
kStateParameters = 0,
2632
kStateCount
@@ -177,6 +183,14 @@ class PMixPlugin : public Plugin
177183
parameter.ranges.max = 1.0f;
178184
parameter.ranges.def = 0.0f;
179185
break;
186+
case kParamSeed:
187+
parameter.hints = 0;
188+
parameter.name = "Seed";
189+
parameter.symbol = "seed";
190+
parameter.ranges.min = 0.0f;
191+
parameter.ranges.max = 1.0f;
192+
parameter.ranges.def = 0.0f;
193+
break;
180194
}
181195
}
182196

@@ -202,6 +216,7 @@ class PMixPlugin : public Plugin
202216
case kParamFadeDurMax: return parameters_.fadeDurMax;
203217
case kParamBias: return parameters_.bias;
204218
case kParamMute: return parameters_.mute;
219+
case kParamSeed: return parameters_.seed;
205220
default: return 0.0f;
206221
}
207222
}
@@ -217,9 +232,15 @@ class PMixPlugin : public Plugin
217232
case kParamFadeDurMax: parameters_.fadeDurMax = value; break;
218233
case kParamBias: parameters_.bias = value; break;
219234
case kParamMute: parameters_.mute = value; break;
235+
case kParamSeed: parameters_.seed = value; break;
220236
}
221237

222238
parameters_ = downspout::pmix::clampParameters(parameters_);
239+
240+
if (index == kParamSeed) {
241+
const uint32_t s = seedToUint(parameters_.seed);
242+
if (s > 0u) engineState_.rngState = s;
243+
}
223244
}
224245

225246
String getState(const char* key) const override
@@ -243,7 +264,7 @@ class PMixPlugin : public Plugin
243264

244265
void activate() override
245266
{
246-
downspout::pmix::activate(engineState_);
267+
downspout::pmix::activate(engineState_, seedToUint(parameters_.seed));
247268
}
248269

249270
void run(const float** inputs, float** outputs, uint32_t frames) override

‎plugins/p-mix/src/dpf/PMixUI.cpp‎

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <algorithm>
44
#include <array>
5+
#include <chrono>
56
#include <cmath>
67
#include <cstdio>
78
#include <string>
@@ -18,6 +19,7 @@ enum ParameterIndex : uint32_t {
1819
kParamFadeDurMax,
1920
kParamBias,
2021
kParamMute,
22+
kParamSeed,
2123
kParameterCount
2224
};
2325

@@ -123,6 +125,9 @@ class PMixUI : public UI
123125
values_[index] = value;
124126
repaint();
125127
}
128+
if (index == kParamSeed && value == 0.0f) {
129+
triggerNewSeed();
130+
}
126131
}
127132

128133
void onNanoDisplay() override
@@ -162,6 +167,11 @@ class PMixUI : public UI
162167
return true;
163168
}
164169

170+
if (seedButtonRect_.contains(x, y)) {
171+
triggerNewSeed();
172+
return true;
173+
}
174+
165175
for (std::size_t i = 0; i < sliderRects_.size(); ++i) {
166176
if (sliderRects_[i].contains(x, y)) {
167177
draggingSlider_ = static_cast<int>(i);
@@ -202,6 +212,7 @@ class PMixUI : public UI
202212
std::array<float, kParameterCount> values_ {};
203213
std::array<Rect, kSliders.size()> sliderRects_ {};
204214
Rect muteButtonRect_ {};
215+
Rect seedButtonRect_ {};
205216
int draggingSlider_ = -1;
206217

207218
void drawBackground(const float width, const float height)
@@ -260,11 +271,14 @@ class PMixUI : public UI
260271

261272
const float innerX = x + 22.0f;
262273
const float buttonY = y + 52.0f;
263-
muteButtonRect_ = {innerX, buttonY, w - 44.0f, 40.0f};
274+
const float innerW = w - 44.0f;
275+
const float halfW = (innerW - 12.0f) * 0.5f;
276+
muteButtonRect_ = {innerX, buttonY, halfW, 40.0f};
277+
seedButtonRect_ = {innerX + halfW + 12.0f, buttonY, halfW, 40.0f};
264278
drawToggleButton(kMuteButton, muteButtonRect_, values_[kParamMute] >= 0.5f);
279+
drawSeedButton(seedButtonRect_);
265280

266281
const float innerY = buttonY + 62.0f;
267-
const float innerW = w - 44.0f;
268282
const float rowGap = 18.0f;
269283
const float rowH = 56.0f;
270284
const float colGap = 18.0f;
@@ -499,6 +513,47 @@ class PMixUI : public UI
499513
commitParameter(kParamMute, next);
500514
}
501515

516+
void triggerNewSeed()
517+
{
518+
using Clock = std::chrono::high_resolution_clock;
519+
const auto ticks = static_cast<uint64_t>(Clock::now().time_since_epoch().count());
520+
auto s = static_cast<uint32_t>(ticks ^ (ticks >> 32));
521+
if (s == 0u) s = 0xDEADBEEFu;
522+
float newSeed = static_cast<float>(s) / static_cast<float>(0xFFFFFFFFu);
523+
if (newSeed <= 0.0f) newSeed = 1.0f / static_cast<float>(0xFFFFFFFFu);
524+
commitParameter(kParamSeed, newSeed);
525+
}
526+
527+
void drawSeedButton(const Rect& r)
528+
{
529+
beginPath();
530+
roundedRect(r.x, r.y, r.w, r.h, 8.0f);
531+
fillColor(28, 48, 68, 255);
532+
fill();
533+
closePath();
534+
535+
beginPath();
536+
roundedRect(r.x, r.y, r.w, r.h, 8.0f);
537+
strokeColor(72, 118, 162, 255);
538+
strokeWidth(1.0f);
539+
stroke();
540+
closePath();
541+
542+
fontSize(13.0f);
543+
textAlign(ALIGN_LEFT | ALIGN_MIDDLE);
544+
fillColor(200, 220, 240, 255);
545+
text(r.x + 14.0f, r.y + r.h * 0.5f + 1.0f, "Seed", nullptr);
546+
547+
const auto s = static_cast<uint32_t>(values_[kParamSeed] * static_cast<float>(0xFFFFFFFFu));
548+
char buf[12];
549+
std::snprintf(buf, sizeof(buf), "#%06X", s & 0x00FFFFFFu);
550+
551+
fontSize(11.0f);
552+
textAlign(ALIGN_RIGHT | ALIGN_MIDDLE);
553+
fillColor(90, 155, 215, 255);
554+
text(r.x + r.w - 14.0f, r.y + r.h * 0.5f + 1.0f, buf, nullptr);
555+
}
556+
502557
void commitParameter(uint32_t index, float value)
503558
{
504559
editParameter(index, true);

‎plugins/p-mix/src/p_mix_engine.cpp‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,25 @@ Parameters clampParameters(const Parameters& raw) {
146146
parameters.fadeDurMax = clampf(parameters.fadeDurMax, kFadeMinFraction, 1.0f);
147147
parameters.bias = clampf(parameters.bias, 0.0f, 100.0f);
148148
parameters.mute = static_cast<float>(clampi(static_cast<int>(std::lround(parameters.mute)), 0, 1));
149+
parameters.seed = clampf(parameters.seed, 0.0f, 1.0f);
149150
return parameters;
150151
}
151152

152-
void activate(EngineState& state) {
153-
state.currentGain = 1.0f;
154-
state.targetGain = 1.0f;
153+
void activate(EngineState& state, std::uint32_t seed) {
155154
state.fadeStep = 0.0f;
156155
state.fadeRemaining = 0;
156+
157+
if (seed == 0u) {
158+
state.currentGain = 1.0f;
159+
state.targetGain = 1.0f;
160+
return;
161+
}
162+
163+
state.rngState = seed;
164+
state.rngState = (state.rngState * 1664525u) + 1013904223u;
165+
const float g = (state.rngState & 0x00800000u) ? 1.0f : 0.0f;
166+
state.currentGain = g;
167+
state.targetGain = g;
157168
}
158169

159170
void processBlock(EngineState& state,

‎plugins/p-mix/src/p_mix_serialization.cpp‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ std::vector<std::string_view> split(std::string_view text, char delimiter) {
4343
} // namespace
4444

4545
std::string serializeParameters(const Parameters& parameters) {
46-
return "version=2\n"
46+
return "version=3\n"
4747
"granularity=" + std::to_string(parameters.granularity) + "\n"
4848
"maintain=" + std::to_string(parameters.maintain) + "\n"
4949
"fade=" + std::to_string(parameters.fade) + "\n"
5050
"cut=" + std::to_string(parameters.cut) + "\n"
5151
"fadeDurMax=" + std::to_string(parameters.fadeDurMax) + "\n"
5252
"bias=" + std::to_string(parameters.bias) + "\n"
53-
"mute=" + std::to_string(parameters.mute) + "\n";
53+
"mute=" + std::to_string(parameters.mute) + "\n"
54+
"seed=" + std::to_string(parameters.seed) + "\n";
5455
}
5556

5657
std::optional<Parameters> deserializeParameters(const std::string& text) {
@@ -83,6 +84,8 @@ std::optional<Parameters> deserializeParameters(const std::string& text) {
8384
parameters.bias = floatValue;
8485
} else if (key == "mute" && parseFloat(value, floatValue)) {
8586
parameters.mute = floatValue;
87+
} else if (key == "seed" && parseFloat(value, floatValue)) {
88+
parameters.seed = floatValue;
8689
} else {
8790
return std::nullopt;
8891
}

‎plugins/p-mix/tests/p_mix_core_tests.cpp‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ void testSerializationRoundTrip() {
175175
parameters.fadeDurMax = 0.5f;
176176
parameters.bias = 35.0f;
177177
parameters.mute = 1.0f;
178+
parameters.seed = 0.75f;
178179

179180
const auto roundTrip = deserializeParameters(serializeParameters(parameters));
180181
assert(roundTrip.has_value());
@@ -185,6 +186,24 @@ void testSerializationRoundTrip() {
185186
assert(std::fabs(roundTrip->fadeDurMax - 0.5f) < 1e-6f);
186187
assert(std::fabs(roundTrip->bias - 35.0f) < 1e-6f);
187188
assert(std::fabs(roundTrip->mute - 1.0f) < 1e-6f);
189+
assert(std::fabs(roundTrip->seed - 0.75f) < 1e-5f);
190+
}
191+
192+
void testActivateSeedBehavior() {
193+
EngineState stateA;
194+
activate(stateA, 0u);
195+
assert(std::fabs(stateA.currentGain - 1.0f) < 1e-6f);
196+
assert(std::fabs(stateA.targetGain - 1.0f) < 1e-6f);
197+
198+
EngineState stateB;
199+
activate(stateB, 0x12345678u);
200+
assert(stateB.currentGain == 0.0f || stateB.currentGain == 1.0f);
201+
assert(stateB.currentGain == stateB.targetGain);
202+
203+
EngineState stateC;
204+
activate(stateC, 0xABCDEF01u);
205+
assert(stateC.currentGain == 0.0f || stateC.currentGain == 1.0f);
206+
assert(stateB.currentGain != stateC.currentGain || stateB.rngState != stateC.rngState);
188207
}
189208

190209
void testLegacySerializationDefaultsMuteOff() {
@@ -211,5 +230,6 @@ int main() {
211230
testFadeProgression();
212231
testSerializationRoundTrip();
213232
testLegacySerializationDefaultsMuteOff();
233+
testActivateSeedBehavior();
214234
return 0;
215235
}

0 commit comments

Comments
 (0)