feat: make filters easier to use
Description
As for now methods like setFilterParameter() have parameters like int attributeId which identifies the index number to the filter parameter. This is difficult to choose since parameters are stored in records for each filter. For example, Freeverb parameters are stored in fxFreeverb in filter_params.dart and are accessible only by knowing the index and reading the code.
Would be nice to have classes easily accessible to get parameter names, mins, maxs, etc for each filter.
I was thinking of something like this:
code
sealed class Filter {
const Filter(
this.filterType,
this.filterName,
this.numParameters,
this.parameterNames,
this.mins,
this.maxs,
this.defs,
);
final FilterType filterType;
final String filterName;
final int numParameters;
final List<String> parameterNames;
final List<double> mins;
final List<double> maxs;
final List<double> defs;
FilterType get getfilterType => filterType;
String get getfilterName => filterName;
int get getNumParameters => numParameters;
String getParameterName(int id) => parameterNames[id];
double getMin(int id) => mins[id];
double getMax(int id) => maxs[id];
double getDef(int id) => defs[id];
}
final class FreeverbFilter extends Filter {
FreeverbFilter()
: super(
FilterType.freeverbFilter,
'Freeverb',
5,
['Wet', 'Freeze', 'Room Size', 'Damp', 'Width'],
[0, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[1, 0, 0.5, 0.5, 1],
);
static int wet = 0;
static int freeze = 1;
static int roomSize = 2;
static int damp = 3;
static int width = 4;
}
[ ... other filter classes ]
then setFilterParameter() could be called as:
code
SoLoud.setFilterParameter(
FreeverbFilter().filterType,
FreeverbFilter.damp,
0.5,
);
The first parameter could be changed to be of type Filter. This will permit us to get rid of the FilterType enum (flagging it as internal) but lead us to a breaking change.
The use of Filter could make sense also for these calls:
code
SoLoud.addGlobalFilter( FreeverbFilter() );
SoLoud.removeGlobalFilter( FreeverbFilter() );
SoLoud.isFilterActive( FreeverbFilter() );
// and also for the coming single audio filters:
sound.addFilter( FreeverbFilter() );
// ... and so on
What are your thoughts about this @filiph?
Hmm, let me think about this. Since there are always at most 1 instances of any type of filter, it feels a bit weird to "add" filters.
I need more time to elaborate, but maybe something like this could work better?
// Global.
SoLoud.instance.filters.reverb.wet = 0.6;
SoLoud.instance.filters.reverb.activate();
// Single audio voice.
sound.filters.echo.activate();
Basically, there's a (lazily initialized) Filters member on both SoLoud and on sounds, and it gives access to the filters.
My thinking is that, since there's unlikely to be a lot of movement in the set of available filters, we can build the N filter classes with ergonomic APIs.
I ended up using one enhanced enum for each filter type. This could be useful for pattern matching for example. The enums look like this:
enum PitchShiftFilter {
wet,
shift,
semitones;
final List<String> _parameterNames = const ['Wet', 'Shift', 'Semitones'];
final List<double> _mins = const [0, 0, -48];
final List<double> _maxs = const [1, 3, 48];
final List<double> _defs = const [1, 1, 0];
String parameterName() => _parameterNames[index];
double min() => _mins[index];
double max() => _maxs[index];
double def() => _defs[index];
int get attributeId => index;
}
These make it easier to access single-parameter values.
Basically, there's a (lazily initialized) Filters member on both SoLoud and on sounds, and it gives access to the filters.
this sounds great! This implies a breaking change since the filter methods will be moved to the Filters class, but I think this new logic worth it?
@alnitak, How do you now track filter statuses between hot restarts?
i.e. I'm following codelab from google io 2024, and there were a line that we could check status with isFilterActive(filter) method, which is now deprecated.
@misterfourtytwo I opened a new issue for that! Thank you.