LI7XI / AudioAnalyzerDocs

Documentations for AudioAnalyzer Plugin.
1 stars 0 forks source link

Unclear parts of the documentation #1

Open d-uzlov opened 3 years ago

d-uzlov commented 3 years ago

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.

LI7XI commented 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.

d-uzlov commented 3 years ago

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

LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago

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.

d-uzlov commented 3 years ago

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:

  1. Handlers that generate arrays of values (where sometimes "array" consists of 1 only value).
  2. Handlers that take arrays of values as input and generate another array as output.
  3. Handlers that doesn't produce any output (for example, image generating 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.

LI7XI commented 3 years ago
Click to expand >This is wrong. Kinda. ... RMS counter, nothing more. >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. >Not a good name. ... I don't propose any certain name, but I would really like to use something more intuitive. >It is misleading. ... 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. >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. Sorry, i was kinda burnt out and didn't know what i was typing. I'm considering a rewrite of many options and pages. Simply applying the [K.I.S.S principle](https://en.wikipedia.org/wiki/KISS_principle). At first i thought this may lead to misconception but actually options are pretty self-explanatory. Also who is going to use RMS or Loudness or other types may already have a background about them, and they would google things out if they want to know more. Things like TargetRate or Filters or Section Variables still need examples. I'll plan this after i finish handler types (fft, loudness, transformers, etc...) to know what to write in the summary (handler types main page and sub-pages). I have more to say but this is the general idea. --- >This is wrong. Kinda. It was copied from a web page lol. Since this is rainmeter, things would be different, it will just work. So i better write as minimum as possible (without being vague). --- >Exactly specified number ... accurate as specified UpdatesPerSecond. >No, this is not related ... used for accurate calculations. Sounds like a wasted computing for me, if so, is it worth it for more accuracy? --- >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. Yep, rainmeter will round float numbers but that doesn't matter, it's not noticeable. --- >>But at most GatingLimit*100% 6 blocks can be discarded. How about writing "But if `GatingLimit 1`, then in some cases handler will only look at one value to describe the loudness." instead of the above. >If GatingLimit is 0.0, then all blocks will always be used. >If GatingLimit is 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. Then write the 3 examples above in GatingLimit option itself? --- >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. Sure. ( ͡° ͜ʖ ͡°) --- --- >Not a good name. I tried to make categories to not cram everything together, they may look overwhelming. I'll think of a better way to organize them. This will come later. --- >It is misleading. >There are generally 3 types of handlers: Should be fixed after i document all handler types.
LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago
> Sorry, i was kinda burnt out and didn't know what i was typing. Don't push yourself too hard. I really appreciate your help, and while the sooner docs will be ready the better, it's not like you are on some time constraints. It will be ready when it's ready. > Also who is going to use RMS or Loudness or other types may already have a background about them, and they would google things out if they want to know more. Well, actually in case of loudness I think that it should be used by people who know nothing on the topic. I remember the time when I didn't know how this all work and I tried to make a skin with some loudness indication, and everything from the AudioLevel _kinda_ worked but at the same time didn't make sense, and there were no real explanations in Rainmeter docs, and everywhere on the internet it was either "use this separate tool" (which is not applicable to Rainmeter) or some very advanced stuff. I would like to fix that. At least my plugin may become the target of "use this separate tool" for Loudness in the context of Rainmeter, and hopefully hopefully there will be some basic easy to understand explanation of what you are doing, but without professional details that are only needed when you are a developer. As for the RMS: yeah, this is intended for advanced users. > It was copied from a web page lol. It's because Loudness meters in general must do frequency correction, but in case of my plugin this correction was separated from loudness meter and is now applicable to whole plugin (on per processing level), which is much more useful and convenient, but loudness meter itself has lost the connection with this frequency correction. > Sounds like a wasted computing for me, if so, is it worth it for more accuracy? Well, maybe the option name is misleading. The thing is, if you hear a sound that is loud most of the time, but is very quiet in some short periods of time, then you will perceive it roughly the same as a sound that is loud all the time, so for more accurate perceived loudness measuring you can just throw out the "very quiet" regions. But how do you determine if a region is quiet? As far as I know, the best method is to calculate loudness for blocks of some size, throw out some of these blocks and use the remaining blocks. And `UpdateRate` option actually determines the size of these blocks. This size corresponds to how often it is updated because you can get a new value as soon as there are new blocks, but that's just a coincidence. The main purpose of this option is to determine granularity of calculations. >How about writing "But if GatingLimit 1, then in some cases handler will only look at one value to describe the loudness." instead of the above. >Then write the 3 examples above in GatingLimit option itself? Well, `GatingLimit 1` is a degenerate case. Even 0.5 may be pushing it. `GatingLimit 0`, on the other hand, just makes Loudness very similar to raw RMS. I just though that showing it as percent will be more intuitive than just saying that "as most GatingLimit values will be thrown out", while for you this turned out to be the opposite.
LI7XI commented 3 years ago
Click to expand >Don't push yourself too hard. >I really appreciate your help, and while the sooner docs will be ready the better, it's not like you are on some time constraints. It will be ready when it's ready. I felt like i took a long time to do it. But **Thanks a lot for your words.** 😁 --- >Well, actually in case of loudness I think that it should be used by people who know nothing on the topic. >but without professional details that are only needed when you are a developer. Sure, it's already easy to understand how this handler works. And it would be even easier once we add examples with gifs. --- >Well, maybe ... granularity of calculations. >Well, GatingLimit 1 ... be the opposite. I ask some questions out of curiosity or if i didn't understand how it works, or if i should add a warning or extra infos. But most of the time this is not needed for users. >Well, GatingLimit 1 ... be the opposite. In [How it works](https://li7xi.github.io/AudioAnalyzerDocs/#/docs/handler-types/loudness?id=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." Then in `GatingLimit` option: If `GatingLimit 1` then ... --- 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.

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.

d-uzlov commented 3 years ago

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.

d-uzlov commented 3 years ago
>You will use handlerInfo Section Variable with File data property. Like this: >However, a more convenient way to obtain file name is to use a child measure with StringValue=Info I would reverse the examples. Using child measure is a preferable and is the only approved way, because in that case Image meter will update automatically. When setting image by name additional actions are needed to force Image meter to update. So using section variables to obtain image path is only for some unusual applications, like, for example, if you want to do something in LUA, and don't really use Image meter. --- >>Source > >Example: >; In parent measure >; In child measure All handler descriptions are in parent measure. Child measures can only provide number and string values. --- >Folder >Path to folder where image will be stored. I think it worth noting that due to `|` being used as option separator, file names can't contain this symbol. --- >Color point syntax: Colors : . There should be note that the syntax is really `Colors arrayOf{ : }`, where array values are separated by `;`. For example, maybe it should be written like this: >Color point syntax: `Colors : ; … ; : `. --- >Q1: "otherwise ignored."? Originally only `BaseColor` and `MaxColor` were used. This greatly limited possible look of the spectrogram. So `Colors` option was introduced. Now I consider it as the main way to specify colors of the image. However, just in case someone used an old skin that uses `BaseColor` and `MaxColor`, if there is no `Colors` option present, then `BaseColor` and `MaxColor` are used to determine colors. I could just remove them from the documentation, but I feel like `Colors` option syntax is a bit too complex for inexperienced users, so intended use for these old options is to make Spectrogram a bit easier to define syntax-wise. What do you think, should these options be removed? Especially since we plan to just drop backward compatibility.
LI7XI commented 3 years ago
Click to expand >I would reverse the examples. Actually i want to remove the first example, i added it just because it was in the docs. The second one is more intuitive and easier to understand. In fact, both examples are not needed, if the `Folder` parameter supports specifying the exact location of the image (even the file name `Folder #@#Images/File.bmp`) then in image meter they can just set the same in `ImageName` option. I guess you decided to name files automatically to avoid conflicts between handlers, but if possible maybe you can add a warning when 2 or more handlers have same `Folder` path. >When setting image by name additional actions are needed to force Image meter to update. I thought it's similar to `DynamicVariables=1`, as if values (X,Y and other options) will get updated on every redraw. --- >All handler descriptions are in parent measure. Child measures can only provide number and string values. >I think it worth noting that due to | being used as option separator, file names can't contain this symbol. Typos, will be fixed. --- >There should be note that the syntax ... >For example, maybe it should be written like this: >What do you think, should these options be removed? I thought about using `BaseColor` and `MaxColor` to explain `Colors` parameter because the syntax is kinda complicated. I would say remove them IF and only if `Colors` have similar syntax to how gradients are defined in shape meter. Because we can simply refer to that. The reason is something like `Colors ` is kinda vague, we are using the word value everywhere. I wanted to use "positions" or as in Shape meter gradients "percentage". But since they are not calculated nor used in same way, i suggest keeping Base and Max color for simplicity, and use them to explain Colors parameter. >Especially since we plan to just drop backward compatibility. Speaking of that, there are 2 things: 1. RGB is in [0, 1] range instead of [0, 255]. When making examples, i copy and paste colors directly, but they get clamped to [0, 1] range. And i have to guess how to transform the 255 to [0, 1] range. 2. Specifying the color space is... unusual. I didn't use it or test it yet, but `@hex$ AABBCC` looks like assembly nightmare code lol. Note that i still don't know where this syntax is used, but i prefer having something like `MixMode` instead of this syntax. --- Aside from all that, there is a major thing, Dynamic variables in Parent measure.
Click to expand What if you want to make a rainbow colors instead of static one? What if you want to use `ParameterName [SomeMeasure]` instead of using `[#Variable]`? like for making something dynamic. I need this to control the sensitivity of the audio. For example, in AudioLevel, sensitivity accepts measures as a value. So i did this: ```ini [MeasureSensitivity] Measure=Calc Formula=Min(#Sensitivity#,(#Sensitivity#-((MeasureAudio-0)/(1-0) * (#Sensitivity#-0) + 0))) ; using parent measure as loudness meter ``` I used this [method](https://stackoverflow.com/questions/345187/math-mapping-numbers) to map numbers (it can also be used to map 255 colors to [0, 1], but it's not intuitive). It worked, but it can't be done in your plugin, so do you have plans to support dynamic variables in parent measure? Or at least, do you have an idea on how to control the audio sensitivity? this is needed in every visualizer. What i mean is, i can control the volume using my media player, but even though there is ValueTransformer handler, there will still be a change. Also i understand this this is different from AudioLevel and it's not a real way in processing audio, but that's the general idea. And lastly, in past i used to go around volume issue by doing this: ![image](https://user-images.githubusercontent.com/55055075/108555251-ed645500-72fd-11eb-8885-e15e3bfb2b13.png) But for some reason, when using a skin that uses your plugin, the Rainmeter audio jumps to 100%, and can't be lowered manually except after closing that skin. Is it a bug or because i didn't use `Source` option?
d-uzlov commented 3 years ago
> In fact, both examples are not needed, if the `Folder` parameter supports specifying the exact location of the image (even the file name `Folder #@#Images/File.bmp`) then in image meter they can just set the same in `ImageName` option. > I guess you decided to name files automatically to avoid conflicts between handlers, but if possible maybe you can add a warning when 2 or more handlers have same `Folder` path. I would rather rename files to include handler name into file name. These files deliberately have unspecified names. These names may change between plugin versions, and it won't be considered breaking changes because the only valid way to know where the file is is to get it from handler. Handler technically _may_ change file name and file location on the fly. It doesn't do it in the current version, but it is allowed. Specifying file name would technically work but it is highly not advised. Since handlers that draw images don't have numeric values, unlike all other handlers, I believe that example is needed for users that don't yet know now they work. > I thought it's similar to `DynamicVariables=1`, as if values (X,Y and other options) will get updated on every redraw. Yes. But when using a child measure to transfer file path it is not needed. --- > The reason is something like `Colors ` is kinda vague, we are using the word value everywhere. I wanted to use "positions" or as in Shape meter gradients "percentage". Well, maybe it's a matter of wording. FFT generate values in range of [0, 1], most handlers keep the ranges of values intact. If you somewhere have specified transform of `db map[from -50 : 0, to 0 : 5]` then your range is [0, 5] for decibel values from -50 to 0. If your transform is just `db` then you are using decibel values directly. The plugin doesn't force you to have values in range [0, 1] everywhere, and the meaning of values can be very different, so I don't know if it's appropriate to name the values in some specific way. In fact, for me using some colors without specifying which values they belong to looks very cryptic. I must remember that the values are always 0 and 1, and then if I want to change anything I have to recalculate reverse actions. Not the hardest thing to do but cryptic and obscure. When specifying which exact values of input match the color, it's also possible to just change the input range. You can specify all the values in range [0, 1] and use the values as percentage. But you are not forced to, alternative ways are avaiable. --- > 1. RGB is in [0, 1] range instead of [0, 255]. Yeah, that's because color in range [0, 255] are very stupid and inconvenient to use. I really don't understand why so many programs use them. Colors are _always_ in range of [0, 1]. However, when you are working with 8-bit canvas, and each color is represented by 8-bit number, you _can_ imagine that each number from 0 to 255 correspond to some real value of a color. If doesn't mean you _should_ imagine it that way. I don't really see _why_ you would do this otherwise than old habits. The **_only_** reason this works is that you specify colors in 8-bit sRGB color space, and your operating system uses 8-bit sRGB internally, and then your monitor shows 8-bit sRGB colors on its display. If anywhere here not 8-bit sRGB is used, you have serious issues with precision. There is not much we can do about this on hardware level but at least we can use adequate values. The **_only_** reason someone used it, like, 50 years ago, is that old computers were very slow, and they couldn't afford to use floating pointer values. Values had to be specified as close to hardware as possible. Like, they seriously couldn't afford convenient values: it would mean that reading the values would take 1 minute instead of 1 second. Now we fortunately don't have such constraints. I'm very angry at at them. No program that respects itself should use [0, 255] range nowadays. The most infuriating thing is that even advanced image editors tend to use integer range. This is so stupid. Using [0, 1023] range is even more frustrating. Not only this doesn't make sense in terms of math and color, but it also can't be explained with backward compatibility, or habits. > And i have to guess how to transform the 255 to [0, 1] range. Oh, you can divide your [0, 255] number by 255. Like 100 → 100/255 → 0.392. You can even to this inside color description itself: as everything in the plugin, it supports math operations. It will work if you just write `100/255`. > I didn't use it or test it yet, but `@hex$ AABBCC` looks like assembly nightmare code lol. This allows you to use different color spaces on each color value. Maybe this is not very useful to use different color spaces, as they are incomparable, but at least it makes color descriptions uniform across all places it is used in. It's not like you know how to specify color in color space X for spectrogram, and then you have to relearn it for Waveform. Syntax is... Well, it's a consequence of backward compatibility. If no strange thing like `@hex$` is found at the beginning then you can parse color as classic color, otherwise you parse it in fancy way. >Note that i still don't know where this syntax is used, but i prefer having something like `MixMode` instead of this syntax. Since backward compatibility will be broken either way, syntax can be changed. I don't know what will be more convenient, though. You have to specify color space either way. I would like to have a clear beginning and an end for color meta info. Something like `hsv 0.5,1,1` would work, but it's not extensible. Also special symbols that can be used here are very limited: symbols `|,:;.` are used as separators elsewhere in the color description, symbols `+-*/^()` are used in math. I don't want to move color space choice outside of color description, in my opinion it would complicate writing options because you have to look up where it is defined every time you want to change color space. Is color space defined for all values in this handler, or are there several options to specify it? What are the ways to specify color space in other handlers? It wouldn't be that much more convenient either way: you would still have to remember names of color spaces, remember the difference between them. It would be much easier to break everything by accidentally changing color space and forgetting to change _all_ color values. Also, color spaces are intended to be advanced feature. Most users are used to sRGB values, so they will likely won't use color space prefix either way. --- > What if you want to make a rainbow colors instead of static one? > What if you want to use `ParameterName [SomeMeasure]` instead of using `[#Variable]`? like for making something dynamic. I didn't test anything specifically, but the plugin should fully support dynamic variables. However, Spectrogram doesn't support dynamic color change for performance reasons. It draws image once and then updates it. IIRC, right now it resets itself on options change. Maybe I could look into this and make it keep image if changes are minor, and new part of the image would be drawn in different way than old part of the image, but you certainly won't be able to change color of the whole image. Though, arguably this is not needed because Rainmeter can do it. --- > I used this [method](https://stackoverflow.com/questions/345187/math-mapping-numbers) to map numbers (it can also be used to map 255 colors to [0, 1], but it's not intuitive). This method is for mapping arbitrary [a, b] range to [c, d]. When you are mapping from [0, Q] to [0, 1] you can just divide your value by Q. Also, what are the issues with transforms? They are pretty much designed for such things. --- > It worked, but it can't be done in your plugin, so do you have plans to support dynamic variables in parent measure? > Or at least, do you have an idea on how to control the audio sensitivity? this is needed in every visualizer. What are the issues with dynamic variables support in plugin other than that images are reset when options change? If it is something complex (like, can be explained in 1-2 lines of text), please, create an issue for it in plugin repository. >What i mean is, i can control the volume using my media player, but even though there is ValueTransformer handler, there will still be a change. What do you want to do that current version of the plugin doesn't allow you to? If it is something complex, please, create an issue for it in plugin repository. --- > But for some reason, when using a skin that uses your plugin, the Rainmeter audio jumps to 100%, and can't be lowered manually except after closing that skin. Is it a bug or because i didn't use `Source` option? I consider changing loudness level for audio visualizers a "broken feature". Plugin prevents loudness level change to provide consistent experience. I should probably add an option for this.
LI7XI commented 3 years ago
Click to expand >I think it worth noting that due to | being used as option separator, file names can't contain this symbol. I thought i made a typo but i can't find any problem, it's just `Folder #@#Images/` and it works well here. >Since handlers that draw images don't have numeric values, unlike all other handlers, I believe that example is needed for users that don't yet know now they work. I removed the example that uses section vars and kept the `MeasureName` method. It looks cleaner. --- >Not the hardest thing to do but cryptic and obscure. Your plugin gives a lot of freedom to use it however you like, but some options should have a specific way of handling them, not just letting the user decide, because we know what works best. I'm talking out of my scope here, but can we clamp the source value to [0, 1]? that way we don't have to worry about recalculations and such. For example in `Colors` option (which i didn't use yet), i found something like `Colors 1.5: 1.0, 0.0, 0.0 ; ...`, 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? 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 of `Colors` parameter. Btw i still don't understand transforms because we didn't discuss it yet, but don't bother yourself, we will come to that later. --- >I'm very angry at at them. Omg chillax bro, i really understand your point, i suggested that because i know users will copy paste from color pickers. And doing something like `BaseColor 210/255,40/255,130/255` felt clumsy to me, no offense, i swear i understand your point, but for the sake of laziness... can that be done internally? i mean letting the plugin deal with dividing values if they are rgb. If it still sound like a bad idea, we can add a note about how users can convert there 255 range colors to [0, 1]. I did image manipulation last year (like literally creating pixels using code) so i understand what you mean, but i didn't think it's all that bad. --- >Syntax is... Well, it's a consequence of backward compatibility. If no strange thing like @hex$ is found at the beginning then you can parse color as classic color, otherwise you parse it in fancy way. Actually i do like the color spaces idea, mostly i use hsl because it's easier for me to specify colors. About the syntax, can it just be hex or hsv without any symbols? and be parsed as any other string? --- >but it's not extensible No, that's more than enough. It's pretty good. Tbh we need to have a bigger picture when deciding to change how existing stuff work, i suggest we finish the docs, then refactor the options that look complicated. --- >Is color space defined for all values in this handler, or are there several options to specify it? What are the ways to specify color space in other handlers? For now i only used and tested spectrogram handler, this is how colors are written in your .rmskin examples `mixMode hsv | colors 0.0: @hsv$ 59, 0.0, 0.1 ; 1.0: 0.9, 0.85, 0.5 ; 1.5: 1.0, 0.0, 0.0`. Colors parameter looks out of law, MixMode is already set but Colors parameter still uses rgb lol. How about this: `Colors hsl 0.0: 25,0.77,0.35 ; 0.5: 160,0.63,0.9 ; ... ` One global color space for the entire parameter, followed by the ``, and lastly the colors. Also we can use that in other parameters like `BaseColor hsl 25,0.77,0.35`, and of course when color space is not specified, sRgb is assumed. --- >Also, color spaces are intended to be advanced feature. Most users are used to sRGB values, so they will likely won't use color space prefix either way. Idk about others, but for me i like using hsl because it's easier to target the color you want. Espacially when you want to make a gradient or shade of colors, you can lower the saturation or lightness without messing with 3 values like in rgb. --- >I didn't test anything specifically, but the plugin should fully support dynamic variables. I meant... using a measure directly as a value, like instead of `ParaName [#var]` we use `ParaName [&measure]`. Right now when trying this, in any handler, it logs a warning about invalid value, and the parameter will not be read. --- >This method is for mapping arbitrary [a, b] range to [c, d]. When you are mapping from [0, Q] to [0, 1] you can just divide your value by Q. Also, what are the issues with transforms? They are pretty much designed for such things. That method performs clamping as well, that's why i used it over dividing. It's not about transforms, i was talking about AudioLevel. --- >If it is something complex, please, create an issue for it in plugin repository. It's simple, instead of using numbers like `SomePara -15` or `0.3`, we use measures as a value: `SomePara [&MeasureName]`. I explained above, doing this outputs an error. --- >I consider changing loudness level for audio visualizers a "broken feature". Plugin prevents loudness level change to provide consistent experience. I should probably add an option for this. 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.

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.

d-uzlov commented 3 years ago

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 of Colors 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 like BaseColor 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 use ParaName [&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.

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago
Click to expand >There is a clamp function in the transforms. If user wants to limit the values, they can do it. But then, what's the point of `db map[from -50 : 0, to 0 : 5]` if we are going to clamp it anyway? --- >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. I should add a note somewhere about that. --- >Well, maybe I could add a "color space" like "legacyRGB" and there could be a switch like `UseLegacyColorsAsDefault` in the handler settings. Good idea but users only know one rgb, don't you think the word "legacy" is kinda confusing? --- >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. Is that why my colors look weird? i used `MaxColor 0,0,1` for testing and it looked greenish. Also i don't understand, why would i change this option or use it in first place? >This is either misleading or simply not true, depending on what you meant by this. I fixed the note. --- >But I don't really like it. >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. You gave me an idea, what about a global color space, but can be overridden for each value? Something like `Colors hsl 0.0: ; 0.5: ; 1.0: sRgb ;` Global color spaces are defined right after the parameter, and can be overridden when defined after the value. Looks messy but it's the general idea. Also i don't think i changed a lot of the current way lol, just got rid of the symbols. --- >I thought about adding sound compression but I don't have any plans for it right now. Alright but what you mean by sound compression? i thought we talking about loudness equalization. 👀
d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago

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.

d-uzlov commented 3 years ago

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.

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago
Click to expand >I don't see how an option for this would be any better than transform with clamp in the end. 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. --- >They could research and find that they were lied to for their entire life. I see this as a good thing. Lmao that sounds cruel but what you mean by color is a lie?😂 >What I want to do is to add DefaultColorSpace option >Any color that have color space prefix will be parsed accordingly. A global option for the entire handler? you just said what i was about to suggest. XD I like the idea so much. --- >You never really ... sound of the wave. Interesting, i'll learn more about it. --- >Please, make it consistent some time in the future. Actually this was a nightmare for me, i'm lost between option/parameter/property. Alright, option is used for rainmeter options: `AnOption=Something`. What about parameter/property? I'm planning to take another look on every page but i still had no idea when to use what. P.S Mostly i change "property" to "parameter", i guess i forgot it this time. --- >Yeah, it should be correct. Btw, about section vars, some arguments are separated (`Some Arg`) and others are connected (`SomeArg`), can you make them all connected? --- >I still feel that the explanation is not clear. Buddy, i'm not gonna lie, i just copy paste that because i still don't understand transforms, i had no idea how `db` works, or what it gives when it's not used with `map`. So i'm waiting to reach transforms part in the docs so we can discuss that.
d-uzlov commented 3 years ago

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":

  1. Separated by line break. Name=value
  2. Separated by | symbol (well, I mostly used this symbol for level 2 separation). type Loudness
  3. Some of the values of level 2 options have named options inside. Like, 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.

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago
Click to expand >I'm sorry, I can't talk about this topic without emotions. LMAO it was hilarious😂😂😂 I watched few videos on video compression and codecs then realized how ridiculous it is to not use [0, 1] rgb range when dealing with more than 8-bit colors. more than [0, 1] range is unusable beyond 8-bits colors. I was learning about SIMD optimizations and memory and such, so this really made me laugh so hard, no worries, we are dealing with just 8-bits anyway lol. >But I guess you can't have everything. We can, but it's not necessary. --- >I don't remember using the word "parameter" for anything specific. You didn't, i used it. I was following the way Shape meter is documented, and they used the word "parameter" to describe a set of options. >I think that we probably should have some name for "not level 1" options. Actually here is how i imagine it: `Option=Para1 | Para2 `. "Option" should only be used for rainmeter options (`Something=Something`). For the bands example: `Para `. I know it's strange to call them parameter arguments but it kinda makes sense. 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 than `ParaArg`? --- >The hardest thing would be not to forget to tell you about everything I remove that was not removed from docs previously. Ez, just make a list of changes and i will update the docs, because the usage will stay the same just names will change. --- >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. I need to get into them asap because i have a lot of misconceptions about many options. ---

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.

d-uzlov commented 3 years ago

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 than ParaArg?

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.

d-uzlov commented 3 years ago

FFT

>The formula for converting BinWidth to AudioLevel FFTSize: FFTSize = SampleRate / BinWidth It should probably be the reverse. We are trying to help users transform their AudioLevel knowledge into AudioAnalyzer knowledge, not the other way around. >Q1: I couldn't find illustrations to explain this, wikipedia seems overwhelming, should we leave it for later? I'm not sure what would be the best way to describe it. One property is that FFT natively updates `BinWidth` times per second. It is probably important to understand that on each update FFT will capture 1000/BinWidth milliseconds of audio wave. So low native update rate always lead to bad temporal resolution, regardless of the final update rate. Another one is how BinWidth is related to bands in BandResampler. Well, I have gave you an examples of how FFT bins get resampled earlier but that example doesn't give user any hints as to how to choose value for BinWidth depending on what bands they use. >Q2: Assuming sample rate is always going to be 48000 (idk how AudioLevel deals with that), will this formula always work? Which formula? `BinWidth= SampleRate / FFTSize` is **_the_** formula to account for changes in sample rate. It will always hold. >idk how AudioLevel deals with that Funny thing, it doesn't. AudioLevel just works differently with different sample rates. You have to change FFTSize manually to get a consistent experience. And before that you need to understand what you are doing, and as far as I'm aware AudioAnalyzer documentation is the only place where an AudioLevel user can learn what they are really doing without diving into hardcore FFT stuff. >OverlapBoost = 1 / (1 - FFTOverlap), where overlap is calculated as AudioLevel FFTOverlap / FFTSize. No. `FFTOverlap` is some big value, like 2048. I think it's best to hold to my original formula: "`OverlapBoost == 1 / (1 - Overlap01)` where Overlap01 is calculated as AudioLevel:FFTOverlap / AudioLevel:FFTSize". I guess you can remove the "AudioLevel:" part but Overlap01 is mandatory to distinguish it from FFTOverlap. >Q3: First time we did AudioLevel FFTSize to BinWidth conversion, but this time we did AudioAnalyzer OverlapBoost to FFTOverlap conversion. shouldn't we do the opposite here? Um, that's literally what the formula is doing: you use FFTOverlap and FFTSize to calculate OverlapBoost. Though, I wonder if such formula is needed in the first place: OverlapBoost is pretty easy to understand on its own, and this conversion only makes it easier to get confused. >Q5: I thought it increases the update rate speed, in other words making values update faster. Well, the fastest update rate of FFT is determined by BinWidth and OverlapBoost. It is the update rate of first cascade. Each consecutive cascade updated 2 time slower (although I'm thinking about makin OverlapBoost adaptive, so that all cascades could update at the same rate). Cascades _kinda_ allow you to have faster FFT update rate by reducing its resolution. Without cascades you must have a very high resolution (= low BinWidth) to have detailed low frequencies. But when you are using cascades, you can specify much higher BinWidth, while still having detailed low frequencies. >Q6: Unless someone have read about it, he won't know what this means, just like when i read it in first time. I feel like that's what default values are for. You are good to go without specifying WindowFunction at all. But if you specify it and use Chebyshev, then there is a default value and are also good to go. If you know what you are doing, or if you feel like experimenting, you can change the parameter to your needs. I don't think we can describe such complex topic in a plugin documentation. Wikipedia page about window functions is big for a reason. >Q7: When using this function, it makes the skin and rainmeter as a whole freezes for few minutes. But it still works. It shouldn't do this. Well, I already know that Chebyshev window generation I used is not very efficient but it shouldn't affect Rainmeter performance when using separate thread. I was able to reproduce it, I will look into this.
d-uzlov commented 3 years ago

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:

  1. You have several BandResampler handlers that sample from the same FFT handler, so one can sample, for example, cascades 1 and 2, and the other 3 and 4.
  2. You just want to see how using deeper cascades look, but you are lazy so you don't want to change options in many other handlers. This case is for testing.

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.

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago
Click to expand >Though, it won't really be a fair comparison as AudioLevel doesn't have most of the features of AudioAnalyzer. No worries, my main focus is FFT type, i don't know a whole lot about RMS or Peak but i'll make few examples to compare them as well. --- >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. Yep i know, i mean it was a general question. >but it's not really complex. I mean, it's just assembly code. I already understand the idea behind it but the lack of resources make it difficult to learn. :( I find it fun to see how these stuff work under the hood. >Algorithms and application architecture affect performance the most. The main reason why i looked at SIMD stuff is repetitive tasks, more specifically, image manipulation (looping on pixels and such). Remember our discussion about making rainmeter read in-memory images? i was thinking about how to do that efficiently, and started researching. >Optimization usually comes from good architecture and choosing right algorithms, not from obscure low-level math. I always use the bare minimum in c++ or any low level language, there is no point in using intrinsics for a + b calculation. But in this case i wanted to do things as fast as possible, since my target is real time stuff. >I have yet to work on a project where vectorization really matters, like, for example, video encoders or scientific math crunchers. Exactly, same here. >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. Umm.. i only wrote "Hello world" until now :P >Also I feel like C++20 melts my mind when I try to comprehend all the things it brings. Only use what your need, _i bet you know this advice already_. For me, C++11 seems enough for most of my use cases. --- --- >It should probably be the reverse. We are trying to help users transform their AudioLevel knowledge into AudioAnalyzer knowledge, not the other way around. Yeah but i didn't write that example, i copied it as it is.😅 I guess it's flipped, i'll fix that. --- >I'm not sure what would be the best way to describe it. Actually it looks okay as it is since we directly compared it to AudioLevel, users don't really need to know how it works, they just want it to work in there skins. >Another one is how BinWidth is related to bands in BandResampler. Don't worry, they don't need to know.🌚 Since the handler is what's taking care of it. P.S You mean it relative? --- >Which formula? >`BinWidth= SampleRate / FFTSize` is the formula to account for changes in sample rate. It will always hold. I mean if the `SampleRate` is consistent (like always gonna be 44100 or 48000), will this formula always work to convert `FFTSize` to `BinWidth`? --- >I guess you can remove the "AudioLevel:" part but Overlap01 is mandatory to distinguish it from FFTOverlap. But it's confusing now, i mean we should say what is `Overlap01`. `Overlap01 = FFTOverlap / FFTSize`? but wait a second, i thought `FFTOverlap = FFTSize / 2`. --- >That's literally what the formula is doing. There was a typo, the formula is flipped. --- >And this conversion only makes it easier to get confused. I wonder, do we even need conversion in first place? i mean AudioLevel FFTSize and Overlap isn't correct since the beginning. I think they can just experiment and they will figure it out. --- >you can specify much higher BinWidth, while still having detailed low frequencies. But it doesn't work that way, visualizations looks a bit out of place. --- >I feel like that's what default values are for. I was talking about the "(attenuation of side lobes in decibels)" part, should i keep it? --- >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. Fixed. --- >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. No u. Trust me, the visualization looked really weird using log, at least in my visualizers. I guess it's better to remove the recommendation and use images to show `Log` and `Linear`. --- >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. Um, i mean, is it something like `Bands Custom `? 0_0 If it works like that, it might be beneficial, but i doubt if someone would use it. --- >You have several BandResampler handlers that sample from the same FFT handler, so one can sample, for example, cascades 1 and 2, and the other 3 and 4. Wait, i'm confused, `1,2 and 3,4` bands or cascades? >You just want to see how using deeper cascades look. Deeper? --- >Maybe I forgot to account for min cascade change in some place. Or probably i didn't use it correctly. There relation with `CascadesCount` option confused me a bit. >If you use it after BandCascadeTransformer or if you don't use cascades in the first place, then this option does nothing. So i should use it after BandResampler but before BandCascadeTransformer?
LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago
> Yeah but i didn't write that example, i copied it as it is.😅 > I guess it's flipped, i'll fix that. I'm positively sure that example in documentation was: >BinWidth and fftSize are connected as following: >BinWidth == sampleRate / FftSize It looked so strange I even double checked that I didn't write it :) --- > P.S You mean it relative? I mean it works in the sense that if you increase band width 2 times then you can increase bin width 2 times and get result of roughly the same quality. I'm not sure if this is useful information. --- > > Which formula? > > `BinWidth= SampleRate / FFTSize` is the formula to account for changes in sample rate. It will always hold. > > I mean if the `SampleRate` is consistent (like always gonna be 44100 or 48000), will this formula always work to convert `FFTSize` to `BinWidth`? This formula will work with any sample rate. It will work with 44.1k, 48k, 192k, 8k or even if you decrease sample rate down to 100 points per second. As I said, this is _the_ formula that allows you to transform FFTSize and BinWidth into each other. Though, its usefulness is not immediately obvious. When you are working with the AudioAnalyzer, you don't need it, everything is done under the hood. However, if you just want to convert your existing AudioLevel skin into AudioAnalyzer without much changes, then you have to do something with the FFT size, because AudioAnalyzer doesn't have fft size option. However, for consistent results user will have to check which sample rate their computer uses. Many people have higher sample rates than 48k. --- > But it doesn't work that way, visualizations looks a bit out of place. This discussion probably worth its own issue page. Maybe there is something wrong with the plugin, maybe it's just documentation doesn't make it clear how something works. --- > I was talking about the "(attenuation of side lobes in decibels)" part, should i keep it? I believe that's an important part. This will only make sense to users that know how this works (aka users who have read wikipedia) but without it even users who know how this works could have hard time guessing what the parameters mean. --- > No u. Trust me, the visualization looked really weird using log, at least in my visualizers. > I guess it's better to remove the recommendation and use images to show `Log` and `Linear`. Based on what I know, this might be due to the fact that you primarily use low frequencies in your skins. `Linear` makes the difference between 100 and 200 look the same as between 16100 and 16200, and you can check how different these sounds are. When you use range from 20 Hz to 20000 Hz `log` is the only way to go. Honestly, I added linear mostly because I could and not because I thought it would be generally useful, but apparently in some cases, like yours, linear works better. --- > Um, i mean, is it something like `Bands Custom `? 0_0 > If it works like that, it might be beneficial, but i doubt if someone would use it. No, it's `Bands Custom `. This will make 5 bands: from bound1 to bound2, from bound2 to bound3, and so on. --- > Wait, i'm confused, `1,2 and 3,4` bands or cascades? Cascades. Let's say that you have 4 cascades. You could have one BandResampler that transforms all 4, and another one that specifically uses only cascade 2. >Deeper? Cascades with bigger indices. --- > So i should use it after BandResampler but before BandCascadeTransformer? It depends on what you want to do. As with everything in the plugin, there is no single defined way to do things, otherwise there wouldn't be so much separate handlers with so much options.
d-uzlov commented 3 years ago
> Colors >Q2: `@hsv$ , , , ` for Alpha? Yes. alpha works the same for all colors. --- >transforms >Example: DB Map[from -70 : 0] Clamp.(?, does the operation order matter? what if clamp came before map or db?) Yes, order matter. This example works like this: ``` x = db(input) x = map(x) x = clamp(x) ``` If you change the order it will work in a completely different way. For example: `map[from 0 : 10] clamp` will transform values from range [0, 10] info range [0, 1] and limit the values to this range `clamp map[from 0 : 10]` will first transform al values < 0 into 0 and all values > 1 into 1, and then multiply the values by 0.1, resulting in [0, 0.1] range. When decibels are involved, the difference will also be non-linear. --- >BandCascadeTransformer >Should be name of BandResampler type handler. Actually, unlike BandResampler and FFT, BandCascadeTransformer will work with any handlers without any issues as long as BandResampler is present somewhere before it. They are not required to be linked _directly_. --- >FFT >Q4: wut? "overlap is 0.5", which overlap? yes i did few corrections but now i'm confused. Well, originally AudioAnalyzer used overlap as a value in range [0, 1] so it was natural to calculate relative overlap from AudioLevel values. Then I changed it to overlapBoost, but I thought that keeping relative overlap in the explanation would be useful. >which overlap Overlap01, as in >Overlap and OverlapBoost are connected as following: >OverlapBoost == 1 / (1 - Overlap01) >where Overlap01 is calculated as AudioLevel:FFTOverlap / AudioLevel:FFTSize
LI7XI commented 3 years ago
Click to expand >It looked so strange I even double checked that I didn't write it :) Yeah i checked the repo and found it correct, but the local docs i have show the following: >Recommended values are [5, 60]. BinWidth-to-fftSize formula is as following: FftSize == sampleRate / BinWidth So if you have an AudioLevel skin with FFTSize=4096 ... Maybe i changed them when copy/pasting :P --- >I'm not sure if this is useful information. Of course it is, it may lead to a confusion if not explained, but i wonder, is there a formula that can calculate this automatically? Like the following, you have `BinWidthVar` (the target BinWidth) and `BandsVar`, then we want to apply a formula that takes `BinWidthVar` and `BandsVar` and calculate the `FinalBinWidth`. In short, set BinWidth once and never worry about it when changing bands count. --- >When you are working with the AudioAnalyzer, you don't need it, everything is done under the hood. >However, if you just want to convert your existing AudioLevel skin into AudioAnalyzer without much changes Well, i guess it's better to remove it, i'm afraid it will lead to a confusion. Because first thing users will do, is downloading examples and checking how it works themselves. Also i want to make a comparison, there i will show what values i used in the examples (`FFTSize` vs `BinWidth`, overlap, etc). --- >However, for consistent results user will have to check which sample rate their computer uses. Many people have higher sample rates than 48k. Hmm, i guess there is a section variable for that, but i wonder what song or mp3 file that have this amount of data, i guess nobody is using wave format nowadays for its huge size. --- >Honestly, I added linear mostly because I could and not because I thought it would be generally useful, but apparently in some cases, like yours, linear works better. I thought the use of Log is boosting high frequencies, but what i see its boosting the low freq ones. >When you use range from 20 Hz to 20000 Hz log is the only way to go. I will tell you about this at the end. --- >No, it's Bands Custom . This will make 5 bands: from bound1 to bound2, from bound2 to bound3, and so on. Wait a second, this is actually cool, as i understand, the example above gives you 6 bands, if `Bound1` was lets say 300, then band1 will provide value in range [0, 1] only when this signal is present, like depending on how strong it is. If i didn't get it horribly wrong, it's cool, but still idk who would use that lol. --- >It depends on what you want to do. As with everything in the plugin, there is no single defined way to do things, otherwise there wouldn't be so much separate handlers with so much options. If possible, i suggest that we have some conventions, not just for this option, but for everything that have more than way to achieve it, for example, the way we get Spectrogram image. I'll talk about this in the other issue. --- >If you change the ... also be non-linear. Um, this works: `Transform db map[from -45 : -0, to [#MinHeight] : [#MaxHeight]] Clamp[Min [#MinHeight], Max [#MaxHeight]]` ```ini MinHeight=5 MaxHeight=100 ``` But when removing clamp: ![image](https://user-images.githubusercontent.com/55055075/109718846-79089c00-7bb0-11eb-919d-968c909e3a0a.png) --- >Then I changed it to overlapBoost, but I thought that keeping relative overlap in the explanation would be useful. Since i suggested to remove the conversion examples, we should remove this one as well right? ---

So yesterday i starting making the .rmskin examples, for now only 1 skin exists in the suit and looks like this:

image

It looked better at first but i started testing BandCascadeTransformer and other options for debugging, there is few things i found:

Click to expand I mentioned that i thought log boosts high freq values, but this is what it's doing (compared to the image above using Linear): ![image](https://user-images.githubusercontent.com/55055075/109720315-950d3d00-7bb2-11eb-9e07-dc15441d0f5d.png) Also, when using log, i get random crashes after setting `BinWidth 10 | OverlapBoost 5 | CascadesCount 4` more than when using linear. P.S use the following settings and play any sounds, rainmeter will instantly crash ```ini Processing=Main Processing-Main=Channels Auto | Handlers MainFFT, MainBR, MainBCT, MainTR, MainFinalOutput | TargetRate 6000 | Filter like-a Handler-MainFFT=Type FFT | BinWidth 10 | OverlapBoost 5 | CascadesCount 2 | WindowFunction hann ; -------------------------------------------------------------------------------------- Handler-MainBR=Type BandResampler | Source Mainfft | Bands Linear [#Bands] [#FreqMin] [#FreqMax] | MinCascade 0 | MaxCascade 0 | CubicInterpolation true ; -------------------------------------------------------------------------------------- Handler-MainBCT=Type BandCascadeTransformer | Source MainBR | MixFunction Average | MinWeight 2 | TargetWeight 30 | ZeroLevelMultiplier 2 ; -------------------------------------------------------------------------------------- Handler-MainTR=Type TimeResampler | Source MainBCT | Attack [#Attack] | Decay [#Decay] | Granularity [#UpdateRate] | Transform db map[from -45 : -0, to [#MinHeight] : [#MaxHeight]] clamp[Min [#MinHeight], Max [#MaxHeight]] ; -------------------------------------------------------------------------------------- Handler-MainFinalOutput=Type UniformBlur | Source MainTR | Radius 2 | RadiusAdaptation 4 ``` Another thing, I guess `CascadesCount` works more like a BinWidth Divider, but only in low frequencies, for examples, the above makes BinWidth = 2.5, but in low frequencies only. Also CascadesCount only works with BandCascadeTransformer (sorry i just realized this). >Q2: Note that RadiusAdaptation doesn't work at all, no matter how much you tweak the values, is it related to using cascades? The reason i said `CascadesCount` acts weird is because i'm using `UniformBlur`, but forgot to change `RadiusAdaptation`. Lastly, i want to mention somewhere in the docs that when using `TimeResampler`, and there is a `ValueTransofmer` handler before it or after it, then no need for that handler, you can just use the `Transform` parameter in TimeResampler. Unless using a new handler in the chain is better.

Check it out AudioAnalyzerExamples.zip, i will add more examples and the comparison with AudioLevel soon.

LI7XI commented 3 years ago

I forgot to mention, i added Filters and FFT Cascades discussion.

d-uzlov commented 3 years ago
> In short, set BinWidth once and never worry about it when changing bands count. I don't think that there is a universal solution. It is linked with OverlapBoost options, bands count, BandResampler's CubicInterpolation option, TimeResampler-type handlers in the chain, BandCascadeTransformer options, and maybe also something else. --- > Hmm, i guess there is a section variable for that, but i wonder what song or mp3 file that have this amount of data, i guess nobody is using wave format nowadays for its huge size. It has nothing to do with the files you are using. Most audio applications that render sound use shared mode, in which case settings (including sample rate) are determined by your sound card settings. If something doesn't match then either application or windows itself must correct it before the sound is played and captured by apps like AA plugin. When I didn't know much about sound I was sure that setting sample rate to 192k would improve the results. Now I know that it would only increase size of audio buffers everywhere, but I have doubts that general public know this. --- > Wait a second, this is actually cool, as i understand, the example above gives you 6 bands, if `Bound1` was lets say 300, then band1 will provide value in range [0, 1] only when this signal is present, like depending on how strong it is. 5 bands. 6 bounds, 5 bands. If you only need to detect whether there was sound or not you are better off with Peak handler, which is almost free, unlike FFT. `Custom` is intended to be used when you need something special. Essentially, this is just to make sure that if Linear and Log band types don't suit you, then you don't have to use custom build of the plugin, or use a lot of separate BandResampler-type handlers. I too can't imagine who and why could you it, because if I could I would probably add it as a prebuilt option. --- > Um, this works: `Transform db map[from -45 : -0, to [#MinHeight] : [#MaxHeight]] Clamp[Min [#MinHeight], Max [#MaxHeight]]` > But when removing clamp: I guess that you have negative values to begin with, so after translation into decibels you have negative infinity. If this isn't correct, please, create a separate issue with full context (at least full parent measure with all variables), here is not the right place to discuss this. --- >Since i suggested to remove the conversion examples, we should remove this one as well right? Year, it would probably be better.

> I mentioned that i thought log boosts high freq values, but this is what it's doing (compared to the image above using Linear): I don't think that "boost" is a right word here. `Log` stretches low frequencies and squeezes high frequencies, to make visuals better match human hearing abilities. [Here is a cool site that I extensively used for testing different options](https://www.szynalski.com/tone-generator/). Check out how distinct and/or similar sounds look in the visualizer with different settings. Unfortunately, FFT produces equal amount of data both in high and low freqiencies, which lead to the issue that you can see on the picture: low frequencies are not detailed enough, and that's exactly why I have spend few hundreds of hours on cascades, to somewhat fix it. > Also, when using log, i get random crashes after setting `BinWidth 10 | OverlapBoost 5 | CascadesCount 4` more than when using linear. There is a known issue with BandCascadeTransformer, it could crash the app when there is not enough precision. I have fixed this issue a week or two ago. Maybe it's the reason of your crashes. I will make an intermediate release today, so that you can write skins at least with the fixes I have already applied. --- > Another thing, I guess `CascadesCount` works more like a BinWidth Divider, but only in low frequencies, for examples, the above makes BinWidth = 2.5, but in low frequencies only Yeah, the effect is similar. Cascades do: 1. BinWidth → effectively BinWidth / 2^cascadesCount, but only on low frequencies 2. CPU usage on average → less than 2 * `CPU usage without cascades` CPU usage worst case → cascadesCount * `CPU usage without cascades` Additionaly there is overhead to processing more values in subsequent handlers. 3. Update rate of FFT: the same in high frequencies, different in low frequencies. In low frequencies you can get update rate anywhere from the fastest (first, or 0th) cascade and all the way to the slowest (last) cascade, depending on BandCascadeTransformer settings. --- > Lastly, i want to mention somewhere in the docs that when using `TimeResampler`, and there is a `ValueTransofmer` handler before it or after it, then no need for that handler, you can just use the `Transform` parameter in TimeResampler. Unless using a new handler in the chain is better. TimeResampler has transforms _after_ its "filtering"/"averaging", but not before. You you have non-linear transforms (that is: anything other than `map`) then the results will be different. Whether it matters is up to skin author but there will be difference. ValueTransofmer after TimeResampler is meaningless, unless you want to get access to intermediate results. Using more handlers is not better, unless you want to get access to intermediate results.
d-uzlov commented 3 years ago

I have posted a new alpha release in plugin repository.

d-uzlov commented 3 years ago

I meant... using a measure directly as a value, like instead of ParaName [#var] we use ParaName [&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:

  1. calc0:Reload is called. calc0 knows its options but its value is 0, because it didn't bother computing it.
  2. plugin1:Reload is called. plugin1 reads someOption and rainmeter tells that its value is 0. Plugin potentially treat it as an error, if 0 is invalid value.
  3. calc0:Update is called. Now calc0 has value 10, but it changes nothing, because the plugin doesn't read options in Update action.
  4. User can't understand WTF plugin doesn't work.

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.

d-uzlov commented 3 years ago

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.

d-uzlov commented 3 years ago

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 for Filter 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]"

d-uzlov commented 3 years ago

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.

LI7XI commented 3 years ago
Click to expand >It has nothing to do with the files you are using. I thought it's related to the bitrate (96/128/192kbps etc..). >but I have doubts that general public know this. Did you mentioned this anywhere in the docs? if not i think you should, i only knew after knowing TargetRate and reading fft cascades discussion. --- >5 bands. 6 bounds, 5 bands. Wut? where is the 6th band? >I too can't imagine who and why could you it, because if I could I would probably add it as a prebuilt option. It's there if someone will need it, i will add examples later on. --- >I guess that you have negative values to begin with, so after translation into decibels you have negative infinity. If this isn't correct, please, create a separate issue with full context (at least full parent measure with all variables), here is not the right place to discuss this. Nope, both of them are positive, min is 5 and max is 100. Is it okay to create an issue even if i misused the options? i mean i don't know if this actually a bug or not. --- >Here is a cool site that I extensively used for testing different options. Check out how distinct and/or similar sounds look in the visualizer with different settings. It's a crime that you didn't tell me about this site🙃 I saved [this](https://www.youtube.com/watch?v=Dbvs8V-13BI) video and used to play it over and over whenever i want to make tests lol. But it's kinda strange, the visualizer i mean, other frequencies get triggered as well while i'm playing only 24hz, shouldn't it only affect 2 or 3 bars on the left? i don't remember but last time i used AudioLevel it didn't have this behavior. Could you take a look at the skin i provided above? ![image](https://user-images.githubusercontent.com/55055075/109850519-45368080-7c5b-11eb-9609-10555cb25b34.png) >Unfortunately, FFT produces equal amount of data both in high and low freqiencies Yeah i read about it, kinda disappointing, but i wonder, is the fourier transform (not fft) has same limitations? >and that's exactly why I have spend few hundreds of hours on cascades, to somewhat fix it. Trust me, it absolutely worth it :), but i still don't know how to use it :[ >Hmm. Hard to say. I didn't think ... new to FFT. Actually that text was pretty informative, my problem is how to use `BandCascadeTransofmer`. What i expected: since values in low frequencies update slowly + they have low res, we can increase there res using `CascadesCount`, but then they will be even more slower, there where we should use `BandCascadeTransofmer` to make the high frequencies slower so they match low freq ones. But i don't actually understand its parameters, also what is `ZeroLevelMultiplier`? i know what it does and how it works, but is it a time? milliseconds? Also isn't there anyway to make `OverlapBoost` suck less resources? it works perfectly some times except when using it CPU = 20% vs not using it 1.5% (even when with 200 band). --- >Yeah, the effect is similar. I will add that in `CascadesCount` option. >Update rate of FFT: the same in high frequencies, different in low frequencies. I thought `BandCascadeTransofmer` will make both low and high freq update at same rate. --- >TimeResampler has transforms after its "filtering"/"averaging", but not before. I will mention that, but idk where, in tips section or the parameter itself? --- >I will make an intermediate release today, so that you can write skins at least with the fixes I have already applied. >I have posted a new alpha release in plugin repository. It works well, do you have a list of changes?
LI7XI commented 3 years ago
Click to expand >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. I read about them but didn't know how they actually work, now i got it, thanks. ^^ >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. ✨Dynamic Sensitivity✨ (i mean audio compression). I wanted to lower the child measure values when volume gets higher using some sorcery math formulas. It works without a child measure, but still have the Reload and update issue. >I believe that AudioAnalyzer reload is pretty optimized for the case when options don't change, so it won't make much difference). Does your plugin support bangs? i mean changing `Map[From -30 : 0]` to `[-25 : 0]` using `!SetOption`. It's pretty much what i want. ---

FFT

Click to expand >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. Does `OverlapBoost` works like interpolation? most of the time it gives you the desired results. --- >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. Alright lol, it actually looked good to me at first. 😅 --- >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.". How about "Humans can only hear frequencies below 20KHz, so (or "which means") sample rate of 44100 Hz is more than enough for your FFT needs." --- >I think it would be easier to understand, if low and high frequencies were separated. Yep, looks good.

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.

LI7XI commented 3 years ago

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.

d-uzlov commented 3 years ago
> Did you mentioned this anywhere in the docs? if not i think you should, i only knew after knowing TargetRate and reading fft cascades discussion. I don't think it's relevant. People can choose what they like. The plugin, after all, does downsampling primarily to make users not think about it. Also, it's a relatively complex topic, and if someone want to know more, youtube has a lot of useful videos. And if someone has агддн decided that they want to use high sampling rates, I'm not the one to convince them that their choice is bad. And maybe on some hardware is makes some difference. > Wut? where is the 6th band? ![Untitled](https://user-images.githubusercontent.com/36223296/109859263-54372600-7c8f-11eb-999e-c9353a9bb225.png) > Is it okay to create an issue even if i misused the options? i mean i don't know if this actually a bug or not. Yeah. > But it's kinda strange, the visualizer i mean, other frequencies get triggered as well while i'm playing only 24hz, shouldn't it only affect 2 or 3 bars on the left? i don't remember but last time i used AudioLevel it didn't have this behavior. Could you take a look at the skin i provided above? It's a small bug in this site. When volume level is 100%, it doesn't generate correct sine waves. Lower the volume from 100% to 95%, for example, and it will work correctly. > Yeah i read about it, kinda disappointing, but i wonder, is the fourier transform (not fft) has same limitations? As far as I know, it's a fundamental limitation of waves. Another name is Heisenberg uncertainty principle, for example. Imagine 2 very similar sine waves. It's hard to tell them apart, and it's not related to how you do this: by FFT, by DFT or even visually. By the way, while it's not directly related, there is another [cool interactive site](https://jackschaedler.github.io/circles-sines-signals/index.html) for fourier trasform related things. I'm not sure if I have mentioned it anywhere earlier. It doesn't have much to do with _using_ FFT, I mostly found it useful for understanding how to code it, but maybe you will think that it's useful, and maybe it worth adding somewhere to tips section. > What i expected: since values in low frequencies update slowly + they have low res, we can increase there res using `CascadesCount`, but then they will be even more slower, there where we should use `BandCascadeTransofmer` to make the high frequencies slower so they match low freq ones. FFT update rate doesn't depend on frequencies. All frequencies update at the same rate. It's only that "deeper" cascades update slower (as they effectively has higher FFT resolution). And BandCascadeTransofmer finds some average between different cascades to combine fast and slow cascades. > But i don't actually understand its parameters, also what is `ZeroLevelMultiplier`? i know what it does and how it works, but is it a time? milliseconds? BandCascadeTransofmer's options should probably be redesigned. I will think about it later. > Also isn't there anyway to make `OverlapBoost` suck less resources? it works perfectly some times except when using it CPU = 20% vs not using it 1.5% (even when with 200 band). OverlapBoost increases CPU usage exactly "OverlapBoost" times. There is nothing you can do about it. If you have `OverlapBoost 15` then you are probably doing something wrong. > I thought `BandCascadeTransofmer` will make both low and high freq update at same rate. Yes. But that discussion is about FFT, not about BandCascadeTransofmer. > I will mention that, but idk where, in tips section or the parameter itself? I'm pretty sure `transform` suboption in the original documentation already have this mention. > It works well, do you have a list of changes? Nope :) Well, there is a list of commits, in which I tried to keep all changes distinct, but it would require to read everything and extract list of changes from it. > Does your plugin support bangs? i mean changing `Map[From -30 : 0]` to `[-25 : 0]` using `!SetOption`. It's pretty much what i want. Yes. Almost all options are read on every reload. I believe, only MagicNumber and threading don't support reload. > Does `OverlapBoost` works like interpolation? most of the time it gives you the desired results. Kinda yes. OverlapBoost and TimeResampler do somewhat similar but distinct things. > How about "Humans can only hear frequencies below 20KHz, so (or "which means") sample rate of 44100 Hz is more than enough for your FFT needs." Yeah, looks good.
LI7XI commented 3 years ago

Filters

>Please, add some notice that this page contains advanced stuff, and is absolutely optional for plugin use. Done. Added warning in Parent page as well (but i don't feel like i wrote them that well). --- >It would probably be convenient to have wikipedia links here, or something. Done, although sometimes Wikipedia intend to be TL;DR so idk if someone would actually read them, also i couldn't find a good blog posts about them. --- >but actually Parent measure documentation says that not-predefined filters must start with custom keyword. Done. --- >I believe that it would be more convenient to have this inline I tried but the text doesn't get wrapped, it gets clipped when reaching the screen edge. --- >It would be very nice of you if you were to rephrase such sentences some time in the future. I'm not a good writer but i'll do my best ;) >It would be better to write "this plugin" or "AudioAnalyzer plugin", or just "AudioAnalyzer". Replaced with just "the plugin". Btw it's a common-sense, nobody would notice the difference. >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. Done. --- >Would better be rephrased to something like "Allowed range of order argument is limited to [1, 5]" Done. ---

Btw, if you have time, i want you to take a second look on the discussion after the fixes, maybe i forgot something.

LI7XI commented 3 years ago

FFT Cascades

>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. Done. --- >Please, replace it with something like "There is no point in high sample rate for FFT". Both shorter and without semantic errors. Replaced with "So there is no point in high sample rate for FFT in this use case." --- >I think it would be easier to understand, if low and high frequencies were separated. Done. --- >>When developing this plugin, I proposed a solution Is it okay? ---

Since the discussion is a bit long, it's better to see the changes in the commit itself.