Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature player filter #61

Merged
merged 30 commits into from
Jul 15, 2024
Merged

Conversation

kashiken
Copy link
Contributor

This PR introduces a filter function for the player.

Purpose of Change

  • To allow the user to preview the audio more precisely.
    • For audio recording use cases, the user can focus on or remove unwanted noise.
    • For music listening use cases, the user can focus on a specific instrument or vocal.

Changes

  • Added bi-quad filters for playback audio path.
    • The added filters are only inserted into the path when enabled.
  • Added PlayerSettingsComponent to set the filter enable and the cutoff frequencies.
    • The user can enable the high-pass and low-pass filters individually.
    • The user can also set the cutoff frequencies.
    • By selecting the "match to spectrogram frequency range" option, the cutoff frequencies automatically match the analyzer frequency range.
      • The user can select the frequency range to preview visually by selecting a range on the spectrogram.

pr_player_filter

Tests

  • I've confirmed the changes visually, and tested the UI operations as well.
  • Some tests have been added to test the new component and settings.
  • The changes conform to all existing tests.

Notes

  • This PR includes the fix of const declaration which I was mentioned in Improve Chart Interaction #57.
  • I replaced some "playerSettingService" to "playerSettingsService" for consistency.

Copy link

@kashiken The CI checks failed. Please review the errors and push fixes.

Copy link

The CI checks passed.

@kashiken
Copy link
Contributor Author

@sukumo28 I also fixed the code in this PR. thanks.

Copy link
Owner

@sukumo28 sukumo28 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR!
There are a few things I would like you to fix.

Copy link

@kashiken The CI checks failed. Please review the errors and push fixes.

@kashiken
Copy link
Contributor Author

kashiken commented Jul 3, 2024

Thank you for your feedback.
Also I'm sorry to have kept you waiting because I found some bugs in my implementation and it took some time to resolve them.

Now, I've finished fixing the code including the bugs I found.
Please kindly check if the changes have been made as intended.

Regarding the way of contribution, I think you mentioned this matter at first, I completely agree with your suggestion to make the issue in advance of PR.
Thank you for maintaining flexibility while making the rules stricter.
Now I have some ideas for new features and haven't started coding yet, so I'll make the issues one by one.

@sukumo28
Copy link
Owner

sukumo28 commented Jul 4, 2024

Thank you!!
I'll check this later.

Copy link

github-actions bot commented Jul 4, 2024

The CI checks passed.

Copy link
Owner

@sukumo28 sukumo28 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your kind PR.
There are a few things I would like you to fix, so please check them.

src/webview/services/playerSettingsService.ts Outdated Show resolved Hide resolved
@kashiken
Copy link
Contributor Author

kashiken commented Jul 8, 2024

Thank you for your feedback.
Let me break down the matter into some aspects.

Proposed UX goal

I think it's better to set the filter frequency independently of analyzer range because fine tuning with cut and try is needed.
In current implementation, changing the filter frequency immediately reflects in the sound the user hears so that the user can adjust the filter frequency by hearing.

Why current implementation has conditional branch

There are two reasons.

Firstly, I used the CHANGE event of HTML input instead of the INPUT event to reflect the change immediately.
However, it causes an event loop. Changing HTML input makes the setting update, and the setting update makes the input UI update.
So, I added the conditional branch, this._hoge !== newValue, which only issues the event when the value is updated.

Secondly, the bi-quad filter cannot work as expected when the frequency set is too low, especially at zero.
So, I validate and limit the value range.
Then, to reflect the value modification to the UI, I added the condition value !== newValue.

How to resolve

From a UI perspective, I suppose it is better to set the filter frequency independently of analyzer range.
However, real-time update with the UPDATE event is not mandatory, we can use INPUT event.
By this approach, we can remove this._hoge !== newValue.

Regarding another condition value !== newValue, I think we can just remove this condition.
Though the modified value will not be reflected in the UI, the frequency is surely limited to make the bi-quad filter work as expected.

Let me summarize my proposal again:

  • Keep the filter frequency setting independently of analyzer range to make it possible to adjust by hearing.
  • Remove conditional branch in the set function with the following countermeasures:
    • Using INPUT event for value inputs to avoid the event loop.
    • Do not reflect the modified frequency to the UI when it is limited.

What do you think about this idea?

@sukumo28
Copy link
Owner

sukumo28 commented Jul 9, 2024

Thank you!!
I understand the reason for implementiation of this PR.
With that in mind, there are a few points I would like you to confirm.

If you read the following, you may think that the UX goal of immediately reflecting value changes for trial and error can be achieved even if you reuse the existing minFrequency and maxFrequency input.
Therefore, it may be a little long and tedious to read, but please check it once.

However, I also write about a compromise at the end, so please check that if you really want to separate the input fields.

About limiting the frequency so that the biquad filter works as expected

In this regard, if you reuse minFrequency and maxFrequency, the reflection in the UI will not be strict.
However, from what I can see, the only special processing that is different from minFrequency and maxFrequency is to set the minimum value to 10, right?
If that's the case, I think it's enough to set 10 in PlayerService if the frequency is too small.
The UI displays a value smaller than 10, so the UI display and the actual sound are different, but since 10Hz is a sound lower than the lower limit of the human audible range and cannot be perceived, this is not a problem.

(If you want the minFrequency or maxFrequency input to become sampleRate/2 instead of the defaultValue when too large an input is entered, you can change the code for the check in the minFrequency or maxFrequency setter.
However, if you do this, I want to make a separate PR and change all the settings ​​in AnalyzeSetting that have this desired behavior. I want you to compromise and keep it as is for this PR.)

About event loops

Will an event loop occur if you use a CHANGE event?

Currently, minFrequency updates setting.minFrequency in the CHANGE event handler.
When updated, AnalyzeSetting fires the AS_UPDATE_MIN_FREQUENCY event in the setter.
The UI receives AS_UPDATE_MIN_FREQUENCY and updates the value displayed in the UI.
The event chain ends there, and it does not appear to have entered an infinite loop.

Thus, I don't think a loop would occur even if the CHANGE event was used.
This behavior was the same when I use the INPUT.
Therefore, it seems strange to say that the CHANGE event was chosen because a loop would occur.

About the choice between the CHENGE event and the INPUT event

Considering that there is no risk of an event loop, the reason for choosing INPUT instead of CHANGE is probably just the difference between whether an event will fire just by changing the value.
With the CHANGE event, the event will not fire unless you press the Enter key, so it may be a little more work when trying and error things out.

About the opinion that you want to separate the input fields to allow fine adjustments by hearing

The desire to separate the input fields because fine adjustments are necessary seems to be due to a misunderstanding between us regarding the timing at which changes in minFrequency and maxFrequency are reflected in AnalyzeSetting.
When the CHANGE event is fired, minFrequency is reflected in AnalyzeSetting immediately, and AS_UPDATE_MIN_FREQUENCY is fired by the setter.
Therefore, by receiving these events with PlayerService, any changes to minFrequency and maxFrequency can be reflected in the Player's behavior immediately, so fine-tuning by hearing is possible.

If there is a concern, it is that, as mentioned above, INPUT events are more convenient for trial and error.
However, isn't it acceptable that just to press the Enter key after entering a number in tne input field? (If you can accept this, CHANGE event is enough)

If this is not acceptable, there is no problem in changing minFrequency and maxFrequency to INPUT events.
(Because this change does not affect the original purpose.)

Policy if you absolutely want to separate the input fields

You are probably the only user of this function, so if you have read all of these comments and still have a strong desire to separate the input fields, we would like to prioritize the user's (your) wishes. I will write the policy in that case here.

As mentioned above, since an infinite event loop will not occur, there is no need to check with an if statement when firing an event such as PS_UPDATE_HPF_FREQUENCY. There is also no need to check with an if statement when reflecting the limited frequency in the UI (even in that case, the event loop will not occur).
So there is no need to give up on displaying the limited frequency in the UI.

For convenience, I think using the INPUT event is more convenient when making fine adjustments, so I think INPUT is fine.
However, if you enter a value smaller than 10, it will be immediately corrected to 10, so you will not be able to delete it if you want to enter a different value, so in that respect the CHANGE event may be more convenient.
Which one you use is up to you as it will not affect any existing code.

@kashiken
Copy link
Contributor Author

Thank you for the feedback!

About event loop

I apologize for the incorrect explanation in my last comment.
I utilize INPUT, not CHANGE event, but I mistakenly stated the opposite.

I was also wrong regarding the matter of event loop itself.
Actually, I faced it in early stage of developing this feature, but it cannot be reproduced now if I remove the conditions, as you said.
I think there was another issue at that time, unrelated to this matter.
Thank you for your explanation in detail so that I could notice my wrong coding.

About input field

For this point, I would appreciate it if you could allow me to maintain the current condition.
Though I am the only user for this feature now, I believe it would be better for any future user to have enable checkbox and frequency input located nearby.
For this, it is also possible to put the filter enable checkbox next to min/max frequency field in the analyzer setting.
However, I think it makes the UI more messy because filter enable is the setting for the player.

About event type

Regarding the event type, I would like to choose CHANGE event.
I've recently tested this feature with a local build and found that it was more disturbing to have the value automatically changed by the validator, compared to the need to press the Enter key after modifying the value, as you mentioned.

Modification

Finally, I modified the code as following:

  • Removing the existing conditions from each set functions.
    • Keep each frequency input as it is.
      • Keep separated from min/max frequencies.
      • Replace INPUT event to CHANGE event and fix test accordingly.

Please confirm above, thank you.

Copy link

The CI checks passed.

@sukumo28
Copy link
Owner

Thank you!

@sukumo28 sukumo28 merged commit 1889222 into sukumo28:main Jul 15, 2024
4 checks passed
@kashiken kashiken deleted the feature-player-filter branch July 15, 2024 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants