Open d-uzlov opened 3 years ago
No, I just mean that we should note that only one of the lines look that way. I wouldn't even say that this is confusing, it's just looks a bit incomplete to me.
I mean, i though it would actually be confusing. Like if they have more than one device and the examples didn't match...
I would say that the first arg specifies the type of information you want to get. [Information about] Current Device, Device List, Value [of a handler], etc. I would say to go with the code blocks, just to show that these are exact values, like names of options or channel names.
Done.
Sample rate and channels may also be represented as "unknown" for some devices when it's impossible to determine that audio device format.
This "unknown" should also be <unknown>
.
This "unknown" should also be
<unknown>
.
Forgot to commit that, now it's fixed. 👍
I'll probably start working on handler types, that's the biggest part of the docs.
I mean, i though it would actually be confusing. Like if they have more than one device and the examples didn't match...
Well, this section variable is intended to be used by people who know how to use LUA. I think that the best way to explain how this work would be to leave a link to a LUA snippet that parses the result. But that's gotta be later, when everything else is ready.
Hello, i added few docs in handler types discussion, for now only Loudness type is fully documented.
Other pages that are documented but still WIP are:
All is done in this commit. It's kinda big commit, but i did them all at same time, i mean all changes are related.
Loudness is a way to measure audio levels based on the way humans perceive sound.
This is wrong. Kinda. Loudness handler doesn't account for how humans perceive loudness. That is mostly done by filter in the processing description. Loudness handler is essentially augmented RMS counter, nothing more.
Q1: Skin updates per second? or when the plugin is running on a separate thread?
Exactly specified number of updates per second.
Handler value will be updated at the same rate as all other handlers, but blocks inside it are updated at independent rate.
If handlers overall are updated at lower rate than UpdatesPerSecond
, then you obviously won't be able to receive value for each block, but the calculations inside handler will be as accurate as specified UpdatesPerSecond.
Q2: I don't know a whole lot about audio topics, so this analogy seems vague to me. Where did you learned about these topics?
Well, there is not much to ebu r 128 loudness metering. See this wikipedia paragraph.
Loudness meter doesn't do anything that is not described in the link above. But it kinda combines three modes that ebu r 128 suggests to use. IIRC, Loudness handler can work in all three modes that ebu r 128 suggests to use, when certain settings are set to certain values.
As to audio topic in general... Well, it's kinda complicated. I'm sure that the most basic knowledge (like, that humans hear not waves but frequencies) you already know. The more advanced part of the topic is, you know, advanced, and I don't think I can explain it. It took quite a time (and quite a number of different explanations of different aspects of audio) to understand it myself.
In my opinion, user doesn't really need to know anything about audio to use Loudness handler. Well, except for: don't forget to use some filter and translate result values into decibels.
Q3: outputting/providing them to child measure? looks a bit long sentence.
Well, handlers have input and output values. Output values can be taken by child measures, section variables or other handlers. There is nothing special about who and where takes output values.
Q4: Is this related to Threading option? What if Threading=Policy UiThread and skin Update=32?
No, this is not related. If handler is updating every 30 ms and Loudness handler must update every 5 ms, then on each update it will generate 6 new values for 6 new blocks of data. These values don't leave the handler, they are only used for accurate calculations.
Q5: wut? i mean how to calculated this? 60 is the equivalent of skin Update=16?
Yes. Well, kinda: rainmeter 16 ms is not really a 60 Hz, because 60 Hz is roughly 16.6666666666(6) ms, but that probably doesn't matter in most cases.
But at most GatingLimit*100% 6 blocks can be discarded. Q6: sorcery formula. I mean i'm not good at math, i can't read it.
If GatingLimit is 0.0, then all blocks will always be used. If GatingLimit 0.5, then half the blocks can be discarded because of gating. If GatingLimit is 1.0 then in some cases handler will only look at one value to describe the loudness.
P.S. Please, use Q# as links in text, it is hard to understand where each question from the bottom of the page belongs to.
Signal processing
Not a good name. Well, technically this is kinda correct. At the end all handlers just process an unknown signal. It doesn't really matter if this signal is audio wave or readings from some voltage meter, for example. However, in the context of the plugin this is too broad. First of all, plugin always work with audio signal, not some generic signal. Secondly, I'm really not sure if general public recognizes the word "signal" in this meaning. Thirdly, most handlers don't do anything with the audio wave. They only transform values generated by other handlers.
I don't propose any certain name, but I would really like to use something more intuitive.
You can use the raw audio signal that is provided by the Signal Processors as it is, but you can refine it to make a better output using Value manipulators (Transformations).
It is misleading. There are generally 3 types of handlers:
All of them can use audio wave, and some of them do even if it is not obvious. All handlers of type 2 in this terminology kinda fall under the category "Value manipulators". Also Transformations can be used in any place in the chain, they aren't intended to only be "final" handlers.
I was kinda going to write an essay on this topic but I don't have the energy to do this. Maybe you could ask me some questions on this topic and formulate it in the documentation as you feel appropriate.
This Process is going to provide a chunk of audio data, separated into channels
I'm not sure if users need to know anything about chunks of data. From user perspective, handlers just work, new values automagically appear on each update cycle, and such under-the-hood details may repel some inexperienced users. I would just say that a processing runs a specified chain of handlers for each specified channel.
Well, handlers have input and output values. Output values can be taken by child measures, section variables or other handlers. There is nothing special about who and where takes output values.
"Applies transformation on values." then we explain that in transform discussion. It would make more sense.
I added few docs on spectrogram type, it's far from "decent", but i'm planning to redo all the examples images at some point.
In How it works section, I think about writing: "However, not all blocks are used, blocks that are almost silent are discarded. You can determine the amount of discarded blocks using GatingLimit option."
Sounds good.
Btw what's the differnce between Loudness, RMS and Peak? i find them very similar and all of them accept Left, Right channels. especially RMS and Peak, they are identical.
Peak: find maximum value over X ms. If there was a silence and just 1 sound sample near maximum, then result will be near maximum. RMS: sum squares of values over X ms, find average, get root of result. Loudness: sum squares of values over X ms (but separated into smaller blocks), throw out too quiet blocks, find average of remaining blocks. Doesn't get root of result, unlike RMS, but in case of using decibels this changes little.
I finished Spectrogram docs, i'll revisit them after documenting fft handler. Adding gifs wasn't easy as i thought, but for now the examples are looking good.
I'm talking out of my scope here, but can we clamp the source value to [0, 1]?
There is a clamp
function in the transforms. If user wants to limit the values, they can do it.
I don't see any reason to to do anything like this globally.
It's user who decides what values mean. There is no general "values are percent" rule or anything like that.
what is the 1.5 here? is it coming from ValueTransformer handler? if so, can't we just clamp it and simply use
Colors 1.0
for the maximum value?
I decided that whatever map
has as source max value is what I consider the "big" value. But it's not the theoretical biggest possible value. If something exceeds that value, then map
will produce values bigger than 1.0 and I want to show it on the spectrogram as different color.
Because when using Base and Max color there is no way to specify the value, so i don't know how
db map[from -50 : 0, to 0 : 5]
will look when using these parameters instead ofColors
parameter.
It will probably look bad.
That's why I like Color option more than these old options. And I don't want to change something this global just to make old (potentially deprecated) options more convenient to use.
These 2 options are designed to work with values in range [0, 1]. Almost everything else in the plugin is not. If user wants to use these options, they can use appropriate values in map
transform before spectrogram.
but for the sake of laziness... can that be done internally?
Well, maybe I could add a "color space" like "legacyRGB" and there could be a switch like UseLegacyColorsAsDefault
in the handler settings.
I don't like the idea of making "255"-range colors as default-default, but I guess an option wouldn't hurt.
Colors parameter looks out of law, MixMode is already set but Colors parameter still uses rgb lol.
Well, MixMode is entirely different thing. It doesn't have anything to do with how colors are defined. Maybe it's easier for you to write colors in RGB but you like the looks of HSV interpolation more.
One global color space for the entire parameter, followed by the
<value>
, and lastly the colors. Also we can use that in other parameters likeBaseColor hsl 25,0.77,0.35
, and of course when color space is not specified, sRgb is assumed.
I will think about it.
But I don't really like it.
In that example above, I know that I want @hsv$ 59, 0.0, 0.1
as first color, and then yellow and red colors, which are easier for me to define in RGB space. Why? Who knows, I wrote it half a year ago, I don't remember why exactly I did this, but I find it convenient.
Also, while I don't know for sure, I suspect why I used HSV as color space for first color and RGB for the rest. It's probably because grayscale colors doesn't have any Hue value. So when using HSV color space for interpolation the results looked very weird. I was forced to use HSV for defining the first color. If users were forced to choose one color space for all colors, then they couldn't fix such things with little effort, like I did.
I meant... using a measure directly as a value, like instead of
ParaName [#var]
we useParaName [&measure]
. Right now when trying this, in any handler, it logs a warning about invalid value, and the parameter will not be read.
I will look into it.
An option? for dynamic volume adjustment? if so, it can be a part of ValueTransformer, something like
Type ValueTransformer | EqualizeVolume true
if that makes sense.
Right now the plugin just enforces 100% volume level for Rainmeter application. I thought about adding sound compression but I don't have any plans for it right now.
By the way
MixMode
Note that any color you can specify (e.g. Colors, BaseColor, etc..) will use the same color space defined here.
This is either misleading or simply not true, depending on what you meant by this. MixMode doesn't affect how colors are read. It only affects how intermediate colors are calculated. Look at the HSV color circle, and pick 2 opposite points on its border. In RGB space interpolation will be a straight line: color 1 → gray → color 2. In HSV space interpolation will be a semicircle.
But then, what's the point of
db map[from -50 : 0, to 0 : 5]
if we are going to clamp it anyway?
You should ask someone who would use map[from -50 : 0, to 0 : 5] clamp
. Obviously, if you want to clamp values then it's unlikely you would use values way outside your clamp range. Though technically that would still work, it would just be much more convenient to use the proper map ranges in the first place.
If I were to add forced clamping as you proposed, then it would be impossible to use ranges outside of [0, 1] in the first place.
Good idea but users only know one rgb, don't you think the word "legacy" is kinda confusing?
What do you propose?
Is that why my colors look weird? i used
MaxColor 0,0,1
for testing and it looked greenish.
Grayscale colors don't have hue. If your other color is defined in RGB space and its components are equal, then it got 0.0 as default Hue value. So in HSV space you got a gradient transition instead of linear rise from gray.
Also i don't understand, why would i change this option or use it in first place?
It depends on what type of color interpolation you want. For example:
Look at the HSV color circle, and pick 2 opposite points on its border. In RGB space interpolation will be a straight line: color 1 → gray → color 2. In HSV space interpolation will be a semicircle.
Set different color spaces to mix option and check the difference yourself. All individual colors are the same in all color spaces, it's just interpolation that differs. If your colors are very close, then it's inlikely you will notice any difference in interpolation type. But I don't see why you would use such close colors: it's tedious and it probably won't look better than just using appropriate interpolation between 2 or 3 colors.
You gave me an idea, what about a global color space, but can be overridden for each value?
Good idea, I think I will add this.
Alright but what you mean by sound compression? i thought we talking about loudness equalization. 👀
These terms are more or less synonyms. However, "sound compression" is always about dynamically boosting quiet sounds, while "loudness equalization" often gets mixed up with equalizers that alter some frequencies in some fixed specific way. Though, I didn't research this much, so maybe I'm misunderstanding something.
If I were to add forced clamping as you proposed, then it would be impossible to use ranges outside of [0, 1] in the first place.
What if... you added an option (in the spectrogram handler) for clamping? ClampSourceRange
or something, when it's true, the max value would be 1 whatever the input value is. When false then just pass the value as it is.
Sorry if i'm asking for too many things.
What do you propose?
Idk, i'm not good at naming things. :P
Good idea, I think I will add this.
Plz, can you add it for any parameter that uses colors? uwu
LineColor hsl <colors>
, BorderColor hsv <colors>
, etc..
while "loudness equalization" often gets mixed up with equalizers that alter some frequencies in some fixed specific way. Though, I didn't research this much, so maybe I'm misunderstanding something.
Actually Idk a lot about sound processing. What i meant by equalization is when audio gets louder, we find a way to dynamically reduce it, and when it's quiet, we leave it as it is.
Btw, i added Waveform type docs. It doesn't have as much images because most options are self-explanatory. Also i will mention that some options are similar or nearly identical to Spectrogram type, so users will see what these option do anyway.
What if... you added an option (in the spectrogram handler) for clamping?
ClampSourceRange
or something, when it's true, the max value would be 1 whatever the input value is. When false then just pass the value as it is.
What do you mean by "the max value would be 1 whatever the input value is"?
If you want simple clamping, then this can be done by transforms before Spectrogram handler. I don't see how an option for this would be any better than transform with clamp
in the end.
Idk, i'm not good at naming things. :P
Me too.
Well, legacyRGB
should never really be used by user.
And UseLegacyColorsAsDefault
should really suggest user that what they know about color is a lie. They could research and find that they were lied to for their entire life. I see this as a good thing.
UPD: well, DefaultColorSpace legacyRGB
would also work that way for suggesting that "255" colors are bad.
Plz, can you add it for any parameter that uses colors? uwu
LineColor hsl <colors>
,BorderColor hsv <colors>
, etc..
What I want to do is to add DefaultColorSpace
option, and every color without color space prefix will be parsed as if it had one from DefaultColorSpace. Any color that have color space prefix will be parsed accordingly.
If you mean that you want to specify colors with a color space everywhere, then you can already do it now. Every color respects its color space prefix in the currently available version of the plugin.
Actually Idk a lot about sound processing. What i meant by equalization is when audio gets louder, we find a way to dynamically reduce it, and when it's quiet, we leave it as it is.
You never really have a "medium" level for anything in computer sound. You only have maximum level, full silence and something in between. There are compression algorithms that boost quiet sounds to max (usually limited by some value, like "boost no more than X times"), and then if it's too loud for you then you lower the master sound of the wave.
By the way
It only makes sense to use this property when
There are many places in the txt documentation where I refer to names that are used in handler descriptions as "property", to separate it from "options" that are used in Rainmeter. You usually use word "option" when referring to things I called "property". I don't have a strong opinion on how these things should be named, but the naming certainly must be consistent throughout the docs. Please, make it consistent some time in the future.
Q1: In some handler types (i guess RMS, Peak and loudness), i said that Transform is "Required", is that true?
No. It usually doesn't make sense not to use some transform, but everything will work without it.
Q2: Is the Block Size example correct?
Yeah, it should be correct.
Waveform always shows values in range [-1.0, 1.0]. If transform makes values outside of this range, they will not be displayed correctly. When writing transform for waveform you may assume that all values are >= 0, and you also should produce values >= 0. Negative values will be calculated the same way, with automatic sign correction.
I still feel that the explanation is not clear.
Waveform doesn't do transform like usual handlers. Transform are designed to work with asymmetric values, but waveform has symmetric values. What Waveform really does is it strips the sign from the value, then gives it to the transform, then restores the sign. Transform in Waveform receives values from [0, 1] range and it should produce values in [0, 1] range to work properly.
This allows you to write something like transform db map[from -70 : 0] clamp
, and it will correctly show both positive and negative values, in a way that is usually used in audio editors with logarithmic view.
If you mean adding a new handler just to clamp the value then i don't find it convenient, i mean, such a small thing doesn't require a new handler.
It is technically possible to get values outside of [0, 1] range without a handler with transforms, however, it's unlikely that someone would do this. So it you have values that are potentially outside if your desired range, then you already have a handler with transform
option, and it will cost you next to nothing to add clamp in the end, if you need it.
Lmao that sounds cruel but what you mean by color is a lie?😂
Colors in range [0, 255] is not about color, it's about how hardware represents color in certain cases. In RGB all three components represent intensity, which ranges from 0 to maximum, and there is nothing that makes number 255 special. Well, technically anything can be a max value. But it's conventional that max value is always 1.0, because nobody want's to remember individual max values for every other thing. Like, you for a few days have been talking how it's inconvenient to have max value not 1.0 for handler values, but 255 as max value for color is somehow the most convenient.
As I said, we only use this number because most systems output 8-bit color, and it's computationally efficient to store color as 8-bit integers, because your monitor won't be able to show it with more precision anyway. However, it really is a storage format. It's unusable for anything but storage. Using it for computations is like using JPEG as intermediate format for image editing: it's slow and you will quickly loose precision. Well, everywhere there are exceptions, but generally 8-bit is not enough precision for most computations. Every time someone writes color R in 255 range, what they really write is "R / 255", just optimized for computer reading at the cost of human reading.
The whole ridiculousness of this 255 thing is very obvious when you try to extend it. So, now you have: 8-bit white (255, 255, 255) 10-bit white (1023, 1023, 1023) 12-bit white (4095, 4095, 4095) 16-bit white (65535, 65535, 65535) And potentially 24-bit white (16777215, 16777215, 16777215) and 32-bit white (4294967295, 4294967295, 4294967295) Convenient, right? All these numbers represent the same color, so it makes sense to have them very distinct, right? Ri-i-ight? And while 24- and 32-bit colors are purely theoretical and are added here just for the sake of absurd, 8-, 10-, 12- and 16-bit color are real and are widely used, and I don't see why anyone who knows anything about color would use then in the form of "real color value in range [0, 1]"×(2^color_bitness). I guess, some people just like to suffer.
Maybe it's time to move from inconvenient letters to very convenient numeric codes for letters. Because computers represent them as bytes, so why would people not want to to write U+0026
instead of this terrible inconvenient &
symbol? Also we would have a lot of fun remembering how the codes from different encodings relate to each other, because it's not like humanity spend billions of dollars and millions of human hours to create computers to do it for us.
Also, 1.0 would be really convenient to write like 0x3ff0000000000000, because that's how computers represent it in memory. Also, it's the same as 0x3f800000. It looks different, but it is the same number. Convenience. 1.1 would then be 0x3ff199999999999a and 0x3f8ccccd. Very handy, don't you agree? We could even use some obscure 10-bit and 12-bit floating point numbers, that would have very different values for the same numbers as 32 and 64-bit numbers. It would be so much fun.
I'm sorry, I can't talk about this topic without emotions.
Alternatively someone could say that 255 is used because of legacy and because people are used to it. So 10-bit numbers could have values like 200.25 and 200.75, from range [0, 255.75], and 16-bit numbers would be from range [0, 256*(1 - 1/2^16)], and it would also be inconvenient to use but at least it would be justified by legacy and human habits, but no: we have the distinct ranges [0, 2^Z] for every Z-bit numbers.
Of course, there is also sRGB—linearRGB translation, which the plugin doesn't do, but I guess you can't have everything.
What about parameter/property?
I believe I tried to use "property" when talking about names for second level options, though I'm not sure I was 100% consistent. I don't remember using the word "parameter" for anything specific. Maybe I used it for the values itself, not named sub-options. I don't feel like this naming convention is ideal, maybe you have some better ideas.
There are generally several levels of "options":
Name=value
|
symbol (well, I mostly used this symbol for level 2 separation). type Loudness
transform map[<level 3 sub-options>]
.Also there are many places where option value is not a set of named options but an enumeration: handlers <enumeration of handlers>
. Or an ordered heterogeneous values: bands log 150 20 20000
.
I'm not sure if there should be special names for individual values of such options.
I think that we probably should have some name for "not level 1" options, because separation by line break and separation by visible symbol are very different things for user, but I'm not sure about different names inside "not level 1" group.
Maybe generic "sub-option" would be better than more distinct words. It could also be used universally across all "not level 1" options.
Btw, about section vars, some arguments are separated (
Some Arg
) and others are connected (SomeArg
), can you make them all connected?
Where semantically-connected values are separated by a space, it's usually because of backward compatibility with the old versions of the plugin. I plan to remove everything related to compatibility later. The hardest thing would be not to forget to tell you about everything I remove that was not removed from docs previously.
i still don't understand transforms
Transforms are easy, custom filters is where the real fun begins ^_^ Though they have entirely different semantic, they aren't building on top of transforms, so maybe it's not that hard.
Sorry for late response, i added FFT
, BandResampler
and UniformBlur
docs, they still don't have examples, but i'm planning to add them in near future.
I'm planning to steal Lano-Visualizer design because it looks cool and simple, so the demonstration will be clear. Also i will compare the performance when using AudioLevel vs AudioAnalyzer (just for pure fun, no sarcasm uwu).
One last thing, how did you managed to wrap your head around this? it made me feel like c++ is easy compared to when looking under the hood lol.
As i understand, a property means something is a part of something, in this case
ParaArg
is a value, is the value a "property" of the parameter? duh, yes it is, i mean is it more clear to use it thanParaArg
?
It's probably better to just have consistent naming with the Shape meter: the syntax is similar, so it will be easier to understand to people that have worked with Rainmeter before, even it there is a semantically slightly better alternative.
Also i will compare the performance when using AudioLevel vs AudioAnalyzer (just for pure fun, no sarcasm uwu).
It will be interesting to see results. Though, it won't really be a fair comparison as AudioLevel doesn't have most of the features of AudioAnalyzer.
One last thing, how did you managed to wrap your head around this? it made me feel like c++ is easy compared to when looking under the hood lol.
Well, that was unexpected. First of all, just to make it clear, in AudioAnalyzer I don't really use vector instructions manually. To be precise, I don't use them by hand in any single place. Secondly, I studied bits and pieces of such things over several years in a university, and I don't feel like this is complex. It's just low level. Which means: it's time-consuming, it's brain-consuming, it requires you to really get to know details of microarchitecture, but it's not really complex. I mean, it's just assembly code. Using intrinsics is just using assembly-like things in higher level languages. Thirdly, it doesn't really matter. Algorithms and application architecture affect performance the most. And most of the time performance isn't even important at all. And I find it harder to create something that is easy to understand and modify than to do optimization. I feel like more then half the time I have spent on the plugin(s) I was just trying to fix the issues with the app architecture and code organization that I made during the original development, because fixing bugs and extending feature set would be a literal nightmare without it. Optimization usually comes from good architecture and choosing right algorithms, not from obscure low-level math. I have yet to work on a project where vectorization really matters, like, for example, video encoders or scientific math crunchers.
P.S. If you think that C++ is not hard enough then you probably haven't tried to read the standard. Not tutorials but the actual standard that is written to be unambiguous and handle all the edge cases. Also I feel like C++20 melts my mind when I try to comprehend all the things it brings.
FFT
BandResampler
Name of source handler. Should be name of some handler from FFT transform chain, so that source handler has many values.
Actually, as I have recently learned from reading sources, in the currently available version of the plugin BandResampler must connect to exactly FFT, otherwise it won't work.
Linear(Recommended1): Will generate evenly distributed values.
No. Linear is technically possible to use when you know what you are doing (though I'm yet to see a task where thiscould be beneficial), but human hearing is logarithmic, and so should be audio visualizers.
Q2: Any example?
bands custom 1 10 50 100 110 1000 5000 7000
Any useful example: no. I personally have never used it. Custom is an option for people who know what they are doing.
MinCascade
I see 2 use-cases for this option:
Honestly, pretty useless option. I will probably remove it.
Q4: … when i tried to use them, the visualization looked really weird in some cases, it was better to just make the BinWidth smaller.
Maybe I forgot to account for min cascade change in some place. But I find it more likely that it is what it really looks like, because, for example, UniformBlur behaves differently with cascades selection and BinWidth change, and maybe some other handlers too.
UniformBlur
Q2: Note that RadiusAdaptation doesn't work at all, no matter how much you tweak the values, is it related to using cascades?
Yes. It only works when you have several cascades. If you use it after BandCascadeTransformer or if you don't use cascades in the first place, then this option does nothing.
I added more docs:
For colors discussion, it's a bit messy, because i used a combination of the old style and the new style, i will update them when the new style is added to the plugin.
So yesterday i starting making the .rmskin examples, for now only 1 skin exists in the suit and looks like this:
It looked better at first but i started testing BandCascadeTransformer and other options for debugging, there is few things i found:
Check it out AudioAnalyzerExamples.zip, i will add more examples and the comparison with AudioLevel soon.
I forgot to mention, i added Filters and FFT Cascades discussion.
I have posted a new alpha release in plugin repository.
I meant... using a measure directly as a value, like instead of
ParaName [#var]
we useParaName [&measure]
. Right now when trying this, in any handler, it logs a warning about invalid value, and the parameter will not be read.I will look into it.
It took me some time to understand why this doesn't work. Fun fact: some Rainmeter measures don't update its values when they first read them. Well, I didn't check everything, but, for example, Calc and Loop delay its update until later.
In case you don't know (and I suspect you don't, as this is not needed for everyday Rainmeter use): Rainmeter has 2 distinct actions: Reload
and Update
.
They are distinct both for built-in measures and meters and for plugins.
Reload is called before Update whenever Rainmeter thinks that settings have changed, and they should be re-read.
Update is... Um, update. It's called depending on skin update rate and update divider options.
The plugin reads all its options in Reload phase.
Let's make an example.
[calc0]
measure=calc
formula=10
[plugin1]
measure=plugin
someOption=[&calc0]
It will work this way:
10
, but it changes nothing, because the plugin doesn't read options in Update action.If plugin were to be reloaded on second update cycle or later, then it would read the correct value, but by default this doesn't happen.
There are ways to force reload action, like !SetOption
bang or DynamicVariables
option, but they have to be performed, and of course, it will have a performance overhead (though, I believe that AudioAnalyzer reload is pretty optimized for the case when options don't change, so it won't make much difference).
P.S. Why did you want to use measures for AA options? It's not like I think that it's a bad practice, I just don't see what benefits this approach has compared to only using variables.
FFT cascades
There is a method of boosting FFT speed: overlapping. Why wait for the whole new big chunk of data when you can reuse old data and update results faster? Well, yes, but that also increases CPU load.
I think it worth adding that overlapping also don't increase time resolution: FFT will be updating faster but will be changing very smoothly even if actual sound changes rapidly.
There is no point in high sample rate. Well, I won't argue where it is useful for some other tasks, but there is no point for FFT.
Oh my god, my terrible writing. Please, replace it with something like "There is no point in high sample rate for FFT". Both shorter and without semantic errors.
Q1: Even though i've read about Big O notation many times, i still don't get it, is there a less precise but more understandable way to explain the concept? (i mean in the places where you mentioned it).
I believe that Big O notation is already maximally simplified.
In case someone doesn't know what O(N*logN)
mean, next paragraph in the same text explains it.
It's technically possible to just remove mention of O(N*logN)
since it's redundant, but I believe it then would look very silly for people who know Big O notation.
So you are a human like me, so you only care about frequencies below 20KHz, then sample rate of 44100 Hz is more than enough for your FFT needs.
I should have tried better when writing it. Please, replace it with "So if you are a human, then you only care about frequencies below 20KHz, and sample rate of 44100 Hz is more than enough for your FFT needs.".
Have effective FFT size N * (2 ^ T).
I think it would be easier to understand, if low and high frequencies were separated. Like:
Have effective FFT size N * (2 ^ T) in low frequencies. Have effective FFT size N in high frequencies.
Q2: Now after we understand how FFT cascades work, how to use this knowledge? is it related to BandCascadeTransformer?
Hmm. Hard to say. I didn't think about it. The main reason this text exists is that FFT cascades are my very own invention. Well, it's very-very possible that a lot of other people use it too, probably under different name, but I didn't find anything even remotely close when googling, so at least this implementation is unique to this plugin. I expect that everyone would be like "WTF is this, how does this work, why do I need this", and this text is the only place where they can get at least partial answers to these questions. Also, when using AudioLevel I didn't understand why FFT is so terrible, and I believe that this text has good explanation of this for people who are new to FFT.
Filters
Please, add some notice that this page contains advanced stuff, and is absolutely optional for plugin use. We don't want to scare off new users who accidentally opened wrong page. Maybe we should also have similar notices on some other pages.
Specifically on the filters page there should be a notice at the very beginning that plugin has predefined filters, and most users don't need to touch custom filters.
They are from two classes: biquad filters and Butterworth filters.
It would probably be convenient to have wikipedia links here, or something.
Examples Processing-Process=Channels ... | Handlers ... | Filter bwHighPass[Order 3, Freq 2000]
Apparently, I didn't mention this on the filters page, and I have already thought I forgot to mention this at all, but actually Parent measure documentation says that not-predefined filters must start with custom
keyword.
Like this: Processing-Process=Channels ... | Handlers ... | Filter custom bwHighPass[Order 3, Freq 2000]
If you are interested, standard filters are also defined as a sequence of these biquad and Butterworth filters.
I believe that it would be more convenient to have this inline
Filter like-a
is a shortcut forFilter custom bqHighPass[q 0.3, freq 200, forcedGain 3.58] bwLowPass[order 5, freq 10000]
Also, not related particularly to this page, but I feel like word "I", "my plugin" and the like sound unprofessional in the documentation. They also make me feel like I'm reading some blog post and not a documentation. It would be very nice of you if you were to rephrase such sentences some time in the future.
However, my plugin alters this behavior
It would be better to write "this plugin" or "AudioAnalyzer plugin", or just "AudioAnalyzer".
I am really not a pro at signal filtering, so if you want to know details of how it works, which filters should you use for which purpose and which values must be in parameters, then just google it. It's also too complex to describe in this documentation.
It would be better to have something like:
Signal filtering is a complex topic, that can't be described and taught shortly, so details on how filters work and recommendations on when and why use which filters is out of scope of this documentation. If you want to use filters but don't know where to start, you are advised to do a research in google.
Maybe the text above should be in the beginning, near the notice that there are predefined filters, instead of the place where I originally put it.
Order is limited in range from 1 to 5
Would better be rephrased to something like "Allowed range of order
argument is limited to [1, 5]
"
FFT
Handler Info
You probably have some old documentation, because last time I changed .txt files was in september 2020, and since then FFT didn't have any handler info documented.
FFT
You probably have some old documentation, because last time I changed .txt files was in september 2020, and since then FFT didn't have any handler info documented.
Um, actually no, i discovered your plugin in September in the forums. But i read the docs and used it in December.
Filters
There is a lot of fixes to be done in that page but i don't have enough energy for it. Mainly because it's out of my scope, although i'll apply the things you suggested.
Filters
Btw, if you have time, i want you to take a second look on the discussion after the fixes, maybe i forgot something.
FFT Cascades
Since the discussion is a bit long, it's better to see the changes in the commit itself.
One issue to discuss everything that's difficult to understand in current version of documentation.
Click to expand
>Channels are present in the audio device(?) From .txt: "list of channels that this processing must process ~~of~~ **_if_** they are present in the audio device". I guess I should have paid more attention to typos. Sorry. In case this is still not clear: I wanted to say that `Channels` option specifies all possible channels that this Process can use, but if some of them aren't present in the audio device (for example, 2.0 headphones don't have Center, LFE, Back and side channels) they won't be present in the Process. --- >TargetRate(Optional): Specify the sample rate of the processed audio stream.(?) What are the issues with this option? --- >Todo: "process description" term still not clear "process description" is exactly 4 options: Channels, Handlers, TargetRate and Filter. It wasn't intended to have any more meaning than this. >Specify description of a sound handler.(?) As with "process description", it's just a set of options. --- >UnusedOptionsWarning >Todo: Can it be renamed to LogErrors? also is the syntax correct? It only affects options that the plugin didn't read. Other log messages are not suppressed with `UnusedOptionsWarning`. For example, if you write `Processing-proc2=channels auto | handlers wave | speed fast`, then there will be a log message `Processing proc2: unused options: [speed]`, and `UnusedOptionsWarning=false` will make such log messages to disappear. Intended usage of this option is to clear the log when you, for some strange reason, have some unused options that you don't want to delete. For me this mostly happens when I want to disable some option, but I don't want to delete it. So I can temporarily rename `filter` to `filter1`, and it won't be parsed. --- >Threading >Policy: Specify the way the plugin will work. I don't know what happened here, but `UiThread` and `SeparateThread` are values of option `Policy`, but they are for some reason formatted as separate options. >Means that each process(?) will create its own working thread. No, there will be only one background thread, that will process everything. --- >OnDeviceDisconnected >Device that was being captured is not in exclusive mode(?). "Device that was being captured is ~~not~~ **_now_** in exclusive mode". I'm sorry, I'm very bad at typos. >OnDeviceListChange >Device was disabled or disconnected.(?) >Or disconnected? what's the diffrence between this and OnDeviceDisconnected event? OnDeviceDisconnected is called when the device you are using was disconnected from the plugin. Nothing to do with the device physical disconnection, it's just that plugin can no longer connect to it. In fact, IIRC, OnDeviceDisconnected is only called when the plugin become disconnected from the device. When one device is disconnected but another is available, then plugin seamlessly switches to that second device and only onDeviceChange is called. OnDeviceListChange is called when _any_ device changed. Changes include, for example, when user pulled the headphone jack from the PC. When OnDeviceDisconnected is called, then OnDeviceListChange is also likely to be called, but it's not guaranteed, and it doesn't work the other way around. ---There are also several notes, that start with "TODO". I'm not sure if these notes are simply for the future or if these notes require some explaining, like above.