-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
PitchShiftEffect: add independent effect #4775
Conversation
This commit adds a new independent pitch shift effect. The pitch shift effect contains one knob for setting up the pitch of a sound. The knob has a fixed range for one octave up or down. The knob works in linear continuous mode. The known issue is, that RubberBand library processing latency needs to be taken into account for different pitch values. Based on that the effect works with an amount of latency.
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.
Thank you. Code looks very good already.
Your header seems to mismatch the implementation
|
After you have addressed my review comments, I suggest you work on getting the usual Rubberband kinks worked out (latency compensation) primarily. |
Thank you for the tips and improvement proposals. I agree with mentioned fixes and then I will focus on solving the latency issue. |
Be warned: I don't know how much we can do about the latency without large changes to the effects system. Please discuss your findings and challenges early. |
SampleUtil::free(m_retrieveBuffer[0]); | ||
SampleUtil::free(m_retrieveBuffer[1]); | ||
|
||
VERIFY_OR_DEBUG_ASSERT(m_pRubberBand) { |
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.
All this code is probably redundant.
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.
I couldn't find the other part of the code, where it should be done. This code was based on how other built-in effects use SampleUtil::free
in their destructors. For example, see:
LinkwitzRiley8EQEffectGroupState::~LinkwitzRiley8EQEffectGroupState() { |
or
~LVMixEQEffectGroupState() override { |
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.
This can be a normal debug assert.
|
||
// static | ||
QString PitchShiftEffect::getId() { | ||
return "org.mixxx.effects.pitchshift"; |
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.
This could become a constant.
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.
And also a QLatin1String
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.
I guess the result is expected to be a QString
so using QLatin1String
would not make sense here.
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.
Mhmm I figured it was faster but looking at the Qt implemenation, you are right its not. We should change that in the future.
|
||
// static | ||
QString PitchShiftEffect::getId() { | ||
return "org.mixxx.effects.pitchshift"; |
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.
I think you mean a QStringLiteral
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.
I used QString
because all other built-in effects use it too.
pitch->setName(QObject::tr("Pitch")); | ||
pitch->setShortName(QObject::tr("Pitch")); | ||
pitch->setDescription(QObject::tr( | ||
"The new pitch of a sound.")); |
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.
I think it is "The pitch shift applied to the sound"
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.
IMO we can tweak the exact wording later. Discussing this is bikeshedding.
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.
That's true, that I planned to improve effect descriptions a little bit later. Anyway, the proposal by @daschuer sounds better, than my version. I will use it. Thank you.
pitch->setDescription(QObject::tr( | ||
"The new pitch of a sound.")); | ||
pitch->setValueScaler(EffectManifestParameter::ValueScaler::Linear); | ||
pitch->setUnitsHint(EffectManifestParameter::UnitsHint::Unknown); |
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.
You may also consider to extend unitsHint. I am afraid it is currently unused but we may consider to inform the user about the units.
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.
I agree, a percentage or a ratio UnitsHint would make sense, but we can do that later.
framesAvailable, | ||
engineParameters.framesPerBuffer()); | ||
SINT receivedFrames = pState->m_pRubberBand->retrieve( | ||
(float* const*)pState->m_retrieveBuffer, |
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.
Remove the cast here as well.
I have just played with it and "Nathan Evans - Wellerman" sounds great with the high pitched voice and the (unwanted) echo and the dry/wet in center position :-) This echo needs to be compensated. Maybe you can actually expose this compensation delay as additional parameter for making funny things without extra CPU cycles. The pitch knob is crackling at the center position which is a known issue. In case of the center position this is a known issue. I have tried to fix it here without success: #4030 Actually the crossfade soluton is a solution at the wrong end. It would be much better to have this solved inside the library. |
Another issue is that your new effect is not visible by default, like all other internal effects. |
I think thats fine until this effect is finished. The problem is that I don't know how we can make it visible by default once we want that.
I agree, I pointed that out earlier, but I don't know how easy that is. Regardless, its still a major priority. |
We have a latency compensation in our bessel filter. It uses
The calculation of the delay is done here. The "issue" is that the delay buffer works only with full sample size delays:
|
Thank you. That's a cool idea with a compensation delay. Maybe I can start a new topic on Zulip for this issue. Do you agree? |
My idea was the same as @Swiftb0y mentioned, to make it visible after the effect will be done. Anyway, I could do it now. I am not sure too if I know, how to make the effect visible by default. |
Thank you for the tips. I just let to stir up the conversation for the first pull request commit. I was fixing the code continuously, so I just will make the last changes for the following fixing commit and I will focus on the latency issue. |
That's reasonable. Since this effect is already fun, it we can merge is any time when you think this PR is finished. And we can discuss the delay issue at Zulip. |
This commit fixes destructor exception specification error and refactors code. The code changes are based on the discussion for the previous PitchShiftEffect commit. Major changes in this commit are: fix destructor explicit exception specification error, complex methods movement from the header file into the .cpp file for PitchShiftGroupState class, use lambda expression for the pitch value assignment, remove unnecessary casts, add the QStringLiteral to the getId method and the PitchShiftEffect the class was set as final.
This commit changes m_pRubberBand stereo buffer size to the count of the frames per buffer for current engine parameters.
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.
LGTM, anyone else willing to take another look before merging?
return; | ||
} | ||
|
||
m_pRubberBand->reset(); |
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.
This is redundant, because it will be called anyway invisibility. No need to care abeut the whole thing of an unique_ptr.
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.
Can you elaborate? This calls RubberBandStretcher::reset()
not, unique_ptr::reset()
... RubberBandStretcher::reset()
will not be called implicitly, it still might be redundant though since we're in the dtor anyways.
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.
Oh, yes I have actually misread the code as m_pRubberBand.reset()
sorry.
But you assumption that it is redundant anyway is also correct. It all ends here
https://github.com/breakfastquay/rubberband/blob/bad529f81e8ae66bd4535a6af392efa38c7fc6b1/src/StretcherImpl.cpp#L222 with almost the same code in the destructor.
SampleUtil::free(m_retrieveBuffer[0]); | ||
SampleUtil::free(m_retrieveBuffer[1]); | ||
|
||
VERIFY_OR_DEBUG_ASSERT(m_pRubberBand) { |
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.
This can be a normal debug assert.
Okay. Anyway, if I remove the duplicated reset of the unique_ptr, IMO it isn't necessary to check it at all. |
The commit removes duplicated RubberBandStretcher::reset() call from the PitchShiftGroupState destructor. The functionality of the reset method is done by RubberBandStretcher on its own.
Thank you! @uklotzde Are your findings also solved? |
ping @davidchocholaty |
Oh, I'm sorry. At the time I created the PR I didn't expect, that the PR will be merged until all effect parts and features will be done. I will remove the [WIP] tag. |
Thank you. That's a nice first step. What will you do next? My approach with the Bessel EQ was this: I have created a 440 Hz sine wave using Audacity, with gaps of silence. Than play it with half dry wet and record it via Mixxx. Than load the recording into Audacy. You should clearly see the latency. I Have noted the latency into a spread sheet. Repeat this with different settings to get a feeling what is the issue we deal with. You may also confirm that with different frequencies. Alternatively you may use a logarithmic sweep of 20 Hz to 22 kHz with gaps to see the whole spectrum in a single file. The optimum solution would be to report the latency to the comon dry/wet Mixer. An intermediate solution would be to put an extra dry/wet knob into the effect. Not sure if this approach fits here as well. What do you think? |
Thank you so much for merging my first PR for Mixxx. It's really motivating for me. Your research for the Bessel EQ really impressed me. Thank you for all the tips. I will beggin with the reseach for the Pitch shift effect latency in the same way. It seems like a good idea to sweep in range of 20Hz to 22 kHz. I will inform you and other developers about the results of the research on the Zulip chat. I wouldn't be afraid of the better optimal solution according to your proposals. IMO the effect fits better into the effect rack and will be more intuitive for the user. |
Adds a new independent pitch shift effect.
The pitch shift effect contains for now one knob for setting up
the pitch of a sound. The knob has a fixed range for one octave
up or down and works in linear continuous mode.
The known issue is, that the RubberBand library processing latency
needs to be taken into account for different pitch values.
Based on that the effect works with an amount of latency.
Implements: lp:1299035 "Add a Transpose / Pitch shift effect"
Reported by: RJ Skerry-Ryan