Skip to content

Commit d3bad45

Browse files
authored
Merge pull request #347 from weefuzzy/fix/params_maxima
Wrapper: constrain param maxima
2 parents 524e923 + 4fa40fa commit d3bad45

File tree

1 file changed

+135
-60
lines changed

1 file changed

+135
-60
lines changed

source/include/FluidMaxWrapper.hpp

Lines changed: 135 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -948,63 +948,95 @@ class FluidMaxWrapper
948948
return MAX_ERR_NONE;
949949
}
950950
};
951-
951+
952952
template <size_t N>
953-
struct Setter<LongRuntimeMaxT,N>
953+
struct Setter<LongRuntimeMaxT, N>
954954
{
955-
static t_max_err set(FluidMaxWrapper<Client>* x, t_object*, long ac, t_atom* av)
955+
static t_max_err set(FluidMaxWrapper<Client>* x, t_object*, long ac,
956+
t_atom* av)
956957
{
957958
if (!ac) return MAX_ERR_NONE;
958959
while (x->mInPerform) {}
959960
x->messages().reset();
960-
auto& a = x->params().template get<N>();
961-
962-
if(!x->mInitialized)
963-
x->params().template set<N>(LongRuntimeMaxParam(atom_getlong(av), a.maxRaw()),
964-
x->verbose() ? &x->messages() : nullptr);
961+
962+
/// Possible scenarios to cope with;
963+
/// 1. object is not yet initialized (i.e @something in the box vs setter
964+
/// being called from outside world
965+
/// 2. initial value could already have been set by argument for 'primary'
966+
/// params: attribute-in-box should 'win' in that case?
967+
/// 3. clients will need to call max() in constructors, so constraints
968+
/// that can increase the value for some need to be applied ASAP
969+
/// 4. for in-box attribute user can pass list (initial, max): if only one
970+
/// is present, then this becomes both initial and max UNLESS max set by
971+
/// argument is bigger
972+
973+
auto a = x->params().template get<N>();
974+
975+
if (!x->mInitialized)
976+
{
977+
index val = atom_getlong(av);
978+
index incomingMax =
979+
std::max<index>(a.maxRaw(), atom_getlong(ac > 1 ? av + 1 : av));
980+
incomingMax = std::max(val, incomingMax);
981+
incomingMax = x->params().template applyConstraintToMax<N>(incomingMax);
982+
a = LongRuntimeMaxParam(val, incomingMax);
983+
}
965984
else
966-
x->params().template set<N>(LongRuntimeMaxParam(atom_getlong(av), a.max()),
985+
{
986+
a = LongRuntimeMaxParam(atom_getlong(av), a.max());
987+
}
988+
989+
x->params().template set<N>(std::move(a),
967990
x->verbose() ? &x->messages() : nullptr);
968-
969-
// x->params().template constrain<N>()
970-
971991
printResult(x, x->messages());
972-
973992
object_attr_touch((t_object*) x, gensym("latency"));
974993
return MAX_ERR_NONE;
975994
}
976995
};
977-
996+
978997
template <size_t N>
979-
struct Setter<FFTParamsT,N>
998+
struct Setter<FFTParamsT, N>
980999
{
981-
static t_max_err set(FluidMaxWrapper<Client>* x, t_object*, long ac, t_atom* av)
1000+
static t_max_err set(FluidMaxWrapper<Client>* x, t_object*, long ac,
1001+
t_atom* av)
9821002
{
9831003
if (!ac) return MAX_ERR_NONE;
9841004
while (x->mInPerform) {}
9851005
x->messages().reset();
9861006
auto& a = x->params().template get<N>();
987-
988-
std::array<index,3> defaults{1024,-1,-1};
1007+
1008+
std::array<index, 4> values{a.winSize(), a.hopRaw(), a.fftRaw(),
1009+
a.maxRaw()};
1010+
9891011
for (index i = 0; i < 3 && i < static_cast<index>(ac); i++)
990-
defaults[i] = ParamAtomConverter::fromAtom((t_object*) x, av + i, defaults[0]);
991-
992-
if(!x->mInitialized)
993-
a = FFTParams(defaults[0], defaults[1], defaults[2], a.maxRaw());
1012+
values[i] =
1013+
ParamAtomConverter::fromAtom((t_object*) x, av + i, values[0]);
1014+
1015+
if (!x->mInitialized)
1016+
{
1017+
if (ac > 3)
1018+
{
1019+
values[3] = std::max<index>(
1020+
{values[0], values[2], values[3], atom_getlong(av + 3)});
1021+
}
1022+
a = FFTParams(values[0], values[1], values[2], values[3]);
1023+
}
9941024
else
995-
x->params().template set<N>(FFTParams(defaults[0], defaults[1], defaults[2], a.max()),
1025+
{
1026+
a.setWin(values[0]);
1027+
a.setHop(values[1]);
1028+
a.setFFT(values[2]);
1029+
}
1030+
1031+
a = x->params().template applyConstraintsTo<N>(a);
1032+
x->params().template set<N>(std::move(a),
9961033
x->verbose() ? &x->messages() : nullptr);
997-
998-
// x->params().template constrain<N>()
999-
1000-
printResult(x, x->messages());
10011034

1035+
printResult(x, x->messages());
10021036
object_attr_touch((t_object*) x, gensym("latency"));
10031037
return MAX_ERR_NONE;
10041038
}
10051039
};
1006-
1007-
10081040

10091041
template <size_t N>
10101042
struct Setter<ChoicesT, N>
@@ -1204,7 +1236,7 @@ class FluidMaxWrapper
12041236
static constexpr index NumOutputBuffers = ParamDescType::template NumOfType<BufferT>;
12051237

12061238
FluidMaxWrapper(t_symbol*, long ac, t_atom* av)
1207-
: mListSize{32}, mMessages{}, mParams(Client::getParameterDescriptors(), FluidDefaultAllocator()),
1239+
: mListSize{32}, mUserMessageQueue{}, mParams(Client::getParameterDescriptors(), FluidDefaultAllocator()),
12081240
mParamSnapshot{mParams.toTuple()}, mAutosize{true},
12091241
mClient{initParamsFromArgs(ac, av), FluidContext()}, mDumpDictionary{nullptr}
12101242
{
@@ -1298,21 +1330,17 @@ class FluidMaxWrapper
12981330
});
12991331

13001332

1301-
while (mMessages.size() > 0)
1333+
while (mUserMessageQueue.size() > 0)
13021334
{
1303-
printResult(this, mMessages.front(), true);
1304-
mMessages.pop_front();
1335+
printResult(this, mUserMessageQueue.front(), true);
1336+
mUserMessageQueue.pop_front();
13051337
}
13061338

1307-
auto results = mParams.keepConstrained(true);
13081339
mParamSnapshot = mParams.toTuple();
13091340

1310-
for (auto& r : results) printResult(this, r);
1311-
13121341
object_obex_store(this, gensym("dumpout"),
13131342
(t_object*) outlet_new(this, nullptr));
1314-
1315-
1343+
13161344
//how many non-signal outlets do we need?
13171345
index numDataOutlets = std::max<index>({NumOutputBuffers,mClient.controlChannelsOut().count,
13181346
Client::getMessageDescriptors().size() > 0
@@ -1732,28 +1760,34 @@ class FluidMaxWrapper
17321760
}
17331761

17341762
auto r1 = mParams.setPrimaryParameterValues(true,
1735-
[](auto idx, long ac, t_atom* av, long& currentCount)
1763+
[this](auto idx, long ac, t_atom* av, long& currentCount)
17361764
{
17371765
auto defaultValue = paramDescriptor<idx()>().defaultValue;
1738-
1739-
if constexpr (std::is_same<std::decay_t<decltype(defaultValue)>,LongRuntimeMaxParam>())
1766+
1767+
if constexpr (std::is_same<std::decay_t<decltype(defaultValue)>,
1768+
LongRuntimeMaxParam>())
17401769
{
1741-
index val = currentCount < ac ? atom_getlong(av + currentCount++) : defaultValue();
1742-
return LongRuntimeMaxParam{val,-1};
1770+
index val = currentCount < ac ? atom_getlong(av + currentCount++)
1771+
: defaultValue();
1772+
val = mParams.template applyConstraintToMax<idx()>(val);
1773+
return LongRuntimeMaxParam{val, val};
17431774
}
17441775
else
17451776
{
1746-
return currentCount < ac ? atom_getlong(av + currentCount++) : defaultValue;
1777+
return currentCount < ac ? atom_getlong(av + currentCount++)
1778+
: defaultValue;
17471779
}
17481780
},
17491781
numArgs,av,argCount);
1750-
for (auto& r : r1) mMessages.push_back(r);
1782+
for (auto& r : r1) mUserMessageQueue.push_back(r);
17511783
auto results = mParams.template setFixedParameterValues<Fetcher>(
17521784
true, numArgs, av, argCount);
1753-
for (auto& r : results) mMessages.push_back(r);
1785+
for (auto& r : results) mUserMessageQueue.push_back(r);
17541786
}
17551787
// process in-box attributes for mutable params
17561788
attr_args_process((t_object*) this, static_cast<short>(ac), av);
1789+
auto results = mParams.keepConstrained(true);
1790+
for (auto& r : results) mUserMessageQueue.push_back(r);
17571791
// return params so this can be called in client initaliser
17581792
return mParams;
17591793
}
@@ -2329,24 +2363,47 @@ class FluidMaxWrapper
23292363

23302364
void decorateAttr(const LongRuntimeMaxT& attr, std::string name)
23312365
{
2366+
/// Apparently we need to be able to specify max<X> attributes *in the box* only as their own
2367+
/// `@max<whatever>` as well as part of a list attached to the main attribute.
2368+
/// This is pretty hairy and makes me sad, given that the logic for this is already over-complex.
2369+
/// Basically, policy is that the biggest proposed maximum 'wins' (think this is safest and easist to enforce)
23322370
std::string maxName = "max" + name;
23332371
std::string maxLabel = std::string("Maximum ") + attr.displayName;
23342372

23352373
using stype = t_max_err(*)(FluidMaxWrapper* x, t_object*, long ac, t_atom* av);
23362374
using gtype = t_max_err(*)(FluidMaxWrapper* x, t_object*, long* ac, t_atom** av);
2337-
2375+
23382376
stype setter = [](FluidMaxWrapper* x, t_object*, long ac, t_atom* av) -> t_max_err
23392377
{
2340-
static constexpr index Idx = N;
2341-
if(ac && !x->mInitialized)
2378+
static constexpr index Idx = N;//for MSVC
2379+
if (ac && !x->mInitialized)
23422380
{
2343-
auto current = x->mParams.template get<Idx>();
2344-
index newMax = atom_getlong(av);
2345-
if(newMax > 0)
2381+
auto a = x->params().template get<N>(); // get the whole param
2382+
index incomingMax = atom_getlong(av);
2383+
if (incomingMax > 0)
23462384
{
2347-
x->mParams.template set<Idx>(LongRuntimeMaxParam(current(),newMax),nullptr);
2385+
incomingMax = std::max<index>(a.max(), incomingMax);
2386+
incomingMax =
2387+
x->params().template applyConstraintToMax<Idx>(incomingMax);
2388+
a = LongRuntimeMaxParam(a(), incomingMax);
2389+
x->params().template set<Idx>(
2390+
std::move(a), x->verbose() ? &x->messages() : nullptr);
2391+
printResult(x, x->messages());
23482392
}
23492393
}
2394+
else
2395+
{
2396+
/// Can't capture here (we need function pointer behaviour),
2397+
/// so need to go through some rigmarole to get attribute name for
2398+
/// warning string
2399+
std::string attrname =
2400+
std::string("max") +
2401+
lowerCase(x->template paramDescriptor<Idx>().name);
2402+
Result onlySetInBox{
2403+
Result::Status::kWarning, attrname,
2404+
" attribute can only be set at object instantiation"};
2405+
printResult(x, onlySetInBox);
2406+
}
23502407
return MAX_ERR_NONE;
23512408
};
23522409

@@ -2382,16 +2439,34 @@ class FluidMaxWrapper
23822439

23832440
stype setter = [](FluidMaxWrapper* x, t_object*, long ac, t_atom* av) -> t_max_err
23842441
{
2385-
static constexpr index Idx = N;
2386-
if(ac && !x->mInitialized)
2442+
static constexpr index Idx = N; //for MSVC
2443+
if (ac && !x->mInitialized)
23872444
{
2388-
auto current = x->mParams.template get<Idx>();
2445+
auto a = x->mParams.template get<Idx>();
23892446
index newMax = atom_getlong(av);
2390-
if(newMax > 0)
2447+
if (newMax > 0)
23912448
{
2392-
x->mParams.template set<Idx>(FFTParams(current.winSize(), current.hopRaw(), current.fftRaw(), newMax),nullptr);
2449+
newMax = std::max(newMax, a.max());
2450+
a = FFTParams(a.winSize(), a.hopRaw(), a.fftRaw(), newMax);
2451+
a = x->params().template applyConstraintsTo<N>(a);
2452+
x->mParams.template set<Idx>(
2453+
std::move(a), x->verbose() ? &x->messages() : nullptr);
23932454
}
23942455
}
2456+
else
2457+
{
2458+
/// Can't capture here (we need function pointer behaviour),
2459+
/// so need to go through some rigmarole to get attribute name for
2460+
/// warning string
2461+
std::string attrname =
2462+
std::string("max") +
2463+
lowerCase(x->template paramDescriptor<Idx>().name);
2464+
Result onlySetInBox{
2465+
Result::Status::kWarning, attrname,
2466+
" attribute can only be set at object instantiation"};
2467+
printResult(x, onlySetInBox);
2468+
}
2469+
23952470
return MAX_ERR_NONE;
23962471
};
23972472

@@ -2454,15 +2529,15 @@ class FluidMaxWrapper
24542529
}
24552530

24562531
index mListSize;
2457-
std::deque<Result> mMessages;
2532+
std::deque<Result> mUserMessageQueue;
24582533
Result mResult;
24592534
void* mNRTDoneOutlet;
24602535
void* mDumpOutlet;
24612536
double mProgress;
24622537
bool mVerbose;
2463-
bool mAutosize;
24642538
ParamSetType mParams;
24652539
ParamValues mParamSnapshot;
2540+
bool mAutosize;
24662541

24672542
Client mClient;
24682543
t_int32_atomic mInPerform{0};

0 commit comments

Comments
 (0)