-
-
Notifications
You must be signed in to change notification settings - Fork 21.2k
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
Fix Ogg/Mp3 import loop point not having single-sample precision #87882
Conversation
Did you discuss integrating their PRs into this? Didn't see any discussion on that on the other PRs, wanting to make sure this has been communicated properly, that's the foundation of collaborative work 🙂 |
I've named them as co-authors in the commit and am in the process of writing comments on those other PRs to ask them to have a look at this one. Not sure if thats the best/most appropriate way to handle this, but I didn't want to just push commits onto their own repositories. I've only had one collaborative PR before, though, so I'd welcome some hints on how one would usually handle these things. 🙂 I'm building on other people's work here and would like this to be understood as a contribution to the discussion. If this PR is giving a different impression, then I did it wrong. 😅 |
934340d
to
946a7cf
Compare
No worries, was mainly that it's best to discuss collaborative work before carrying it out, to not step on anyone's toes or make anyone feel left out or supplanted 🙂 my suggestion would be to simply discuss things with the others and decide together I haven't compared the code here between the commits, so not saying this is anything of the sort, but for example some might be upset if someone else fixes a relatively small part of their work and makes a new PR, rather than discussing it with them Simply put in my view the best way to avoid any confusion or conflict is to discuss first, but to make clear, not any kind of criticism of the way you acted here |
Yes, that makes a lot of sense. I suppose it's a bit of an edge case, because while I didn't consciously copy any code, the ideas are of course similar, since they come from the same discussion. @RustyRoboticsBV, if you prefer, I'd be happy to merge these changes into your PR. I'd just like you to have a chance to look at them first. I'd say the most relevant differences are:
Actually, a question to the room at large: Is renaming an import option a breaking change (when the previous option is still supported but no longer listed)? |
95ddae3
to
103679b
Compare
My toes aren't stepped on, no worries! Conceptually I'm a big fan of these changes (I was tinkering on something very similar actually, so we're pretty much on the same page here). I'm going to properly check this PR out tomorrow and see if I can find any issues. But at a glance, I very much support this PR. I'll leave mine open for now until this gets merged. |
103679b
to
eaf4126
Compare
Ok, I've given the new importer a spin, and it's looking good! A couple of thoughts: I noticed that the I also noticed a small bug with the way the Lastly, I found a bug where the advanced editor doesn't pass the loop point to the playback stream until the Oh, and the class DB is complaining in the console about But overall: great work! |
Thanks for the detailed feedback!
Yes, I feel that's still a bit of a shortcoming, too. I left it at 0.001 just for the sake of consistency with the previous user experience, but one thing I really liked about MJacred's PR was that you could just paste whatever seconds value you had in the clipboard from where you did your calculations while still having the option to step in millisecond intervals if you wanted to. The way it is now, you have to use samples if you need more than millisecond precision. Not sure if it's feasible to ask for a change to a basic GUI control for a PR like this. Just changing the step value to something finer would be an alternative that doesn't require changes outside of the dialog itself.
Good catch! I had a feeling that piece of code was a bit dodgy when I committed it. Looking at it now, maybe I should do it the other way around anyway: Convert the internal value to the unit, then round the result according to the step value, then check if that number matches what's already in the field. If so, don't update the internal value. That way, we could use a very generous epsilon of half a step without fear of clamping too eagerly.
Ah, yes, the initial change notifications are probably not enough, because the stream needs to be initialized once, even though the internal and the displayed value initially already match. Makes sense, thanks!
Do you get that on the very latest commit? I've spend some time last night updating the ClassDB statements until the code checks were happy and for me, these errors are gone now. Maybe these issues are already fixed if you pull again? |
When it comes to one of my biggest pet peeves with Godot, I would be thrilled if someone were to propose a solution to the underlying control. Inspector rounding being tied to step size is highly frustrating to me. If I paste or type in a value manually, the control should not round. The fact that Godot rounds displayed data and manually-input data is wrong, except possibly in the case of step=1 to ensure values are integers the other thing is it would be nice for the control to display the value in full precision regardless of step size, but if it is == equal to the rounded value, we should make sure it displays the rounded value. This way, a user using the slider will never see the extra precision but it can still be typed in manually and displayed. If you'd like, I can type this into its own proposal |
eaf4126
to
ef2fd69
Compare
…es, or beats. Made loop point offset import settings be serialized as int to retain the required precision. Co-authored-by: RustyRoboticsBV <[email protected]> Co-authored-by: MJacred <[email protected]>
ef2fd69
to
125b8f3
Compare
I've now updated the commit with these fixes:
I think that'd be great! Then we can separate these topics and later integrate the updated control into this dialog to make things a bit nicer. Blender seems to use three different precisions: display precision, value precision, and step precision. Different input fields use different values, but for a scale factor, for instance, if the field contains the value 1.23456, then
It's possible that the actual value is never rounded in Blender, so that second precision may just always be the max precision of the data type. |
Yeah, turns out it was an older version. It's indeed gone on the latest version. |
<method name="_get_sampling_rate" qualifiers="virtual const"> | ||
<return type="int" /> | ||
<description> | ||
</description> | ||
</method> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<method name="_get_sampling_rate" qualifiers="virtual const"> | |
<return type="int" /> | |
<description> | |
</description> | |
</method> | |
<method name="_get_sampling_rate" qualifiers="virtual const"> | |
<return type="int" /> | |
<description> | |
Return the sampling rate of the audio stream in samples per second. | |
</description> | |
</method> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although #86958 has yet to be merged, if you want to be consistent:
<method name="_get_sampling_rate" qualifiers="virtual const"> | |
<return type="int" /> | |
<description> | |
</description> | |
</method> | |
<method name="_get_sampling_rate" qualifiers="virtual const"> | |
<return type="int" /> | |
<description> | |
Overridable method. Should return the sampling rate of this audio stream, in samples per second. | |
</description> | |
</method> |
Heyo, I am most likely going to favor my own solution for this: As you can't really rely on sample precision for looping these types of files properly given their nature. This also does a small cross fade for the loop, so its much easier for users to do the actual looping without having to use sample level precision. |
To clarify, it is entirely intended that OGG/MP3 have one type of looping, and WAV has another. Both file types are used for different purposes and there is no interest in unification. |
Oh, I hadn't checked for previous pull requests on this topic, sorry about that. Yes, that should already solve most of these cases. I'm not quite sure how we'd then handle variable-tempo music, but that may be a bit of a non-standard case (although I do have one of those in my project). In any case, the previous workaround of baking parts of the loop into the file until the loop point aligns with a millisecond would still work for those. If there are no objections, I'll close this in favor of #86517. |
@bs-mwoerner: Sorry that I couldn't get to your PR sooner. Personally, I'm in favour of saving loop point as samples ( Regarding @reduz' PR, as I stated here, I don't see how his code affects non-BPM looping. If there is some code that enables it for "normal" looping, then I can't find it. This
is the only line I can find that triggers this fading. And it's limited to BPM, because this line
prevents the if-clause from being triggered for "normal" looping (because If the fading @reduz mentioned is done in AudioServer (or if I'm blind), then nevermind this objection. But I'm still in favour of samples. |
No worries and thanks for taking a look! As I understand it, the idea is that accurate looping is only required for music and most music uses a fixed tempo. For these cases, you can identify the loop point with sample accuracy if you combine the bpm value and the beat count. Since these are stored as two separate values, the limited serialization precision for floats should not be a factor. For example, a loop point at beat 8 at 134 bpm would be at 3.58208955... seconds, which you can't enter into the field and which would be truncated during saving, but you can enter 134 and 8 separately, which can both be safely stored and loaded and which will later be combined to give the precise value. The fade, I think, is only intended to happen when you make use of the option to loop before the end of the file (by specifying a corresponding bpm and beat number). The stream would then play a bit beyond the end of the loop while fading back into the start of the loop. If the file was produced to be loopable, then most likely the file end is the loop end and no fade is done. The only problem is with files that don't have a constant bpm (sound effects or variable tempo music). For these cases, you'd either have to edit the file to align the loop point with a position that can be entered into the dialog or set the loop point manually in a script after the import. I still like your full-precision Range modification, although this probably only makes sense in combination with some modification that makes sure that the value survives until a possible reimport (such as internally switching to integer, not sure if there are other ways). |
@bs-mwoerner: That's how I understood his PR as well, that's why I requested a change of its title here. Someone who wants to loop (with a loop offset), but does not want to use BPM logic, won't benefit from that PR at all, and therefore it's no replacement for this PR.
Indeed, saving as sample (int), the range modification, and double (de-)serializing should be discussed further. The latter two can be handled in a different place (rocket chat / godot-proposal / in my PR). |
@MJacred Well, if you are looping non-music (like SFX) then there is not much of a point in having sample precise looping as long as the stream does a quick fade. Looping audio files to sample precision to avoid clicks is a horrible experience, only really justified if you are doing it for instrument samples and the likes, which is not something you will do with MP3/OGG. WAV is far more suited for it. |
As said, will make the fade also work with non BPM looping btw. |
Ok, I don't have that kind of experience so I can't comment on that. I know that Audacity can pull off seamless looping with loop offset, but I have no idea what they do under the hood to make it work (which may not be feasible for game engines or may be exactly what you do). I'll gladly test looping for non BPM looping. Building on that, I'd refactor #87860 to just "Use double for loop_offset consistently" and increase steps from |
I've played around with this a bit and think using approximate loop points combined with the fade sounds the same as sample-accurate looping, so supporting sample counts should not be necessary. Here's a loop using sample counts and no fade: SamplesWithoutFade.mp4I can convert this into the bpm system by
This is the loop with bpm fade: BeatsWithFade.mp4The timing being off by a millisecond or two is not noticeable and the fade glosses over any discontinuities in the wave form, so I think this is just fine. For constant tempo files, you don't even have to work out the average tempo. |
Why close? |
Oh, sorry, I thought the rationale was clear from the recent comments or I would have added an explanation: reduz's existing PR #86517 fades over cracks around loop points, so if that's implemented (especially if made to work with non-BPM looping), there's probably no good reason to change the internal data structures to accommodate sample-accurate loop points. And since there weren't any unresolved objections as far as I can tell, I thought it was time to close this PR in favor of #86517. |
Fixes #87427. Possibly also contributes to addressing godotengine/godot-proposals#84.
This is another suggestion for how to address the limited loop point precision in the Audio Stream Importer dialog for Ogg and Mp3 and supplements the ideas in #87554 and #87860. It adds a drop down that switches the unit used for the Offset field between Seconds, Samples, and Beats.
Advantages are:
.import
file does not retain the fullfloat
precision across the conversion to and from a string.)loop_offset
import option to be namedloop_offset_samples
to differentiate what method was used for the import. The dialog and the resource importers work with either option but preferloop_offset_samples
if it is present.Issues are:
AudioStream
class to expose the sample rate so that the dialog can do the necessary conversions.