@@ -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