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

TripleOscillator: Phase Randomization #4369

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

SecondFlight
Copy link
Member

@SecondFlight SecondFlight commented May 22, 2018

This PR would add phase randomization to TripleOscillator. When enabled for an oscillator, the phase of the left/right voices are separately randomized. The amount is controlled by a knob - 100% means complete randomization, while 10% means it can move up to 10% of one period. The default is 0% which will have no effect.

This is a feature I've wanted for quite some time. My reasoning behind adding this is as follows:

  • FL's 3xosc has a similar feature. It is a global phase randomization option instead of per-voice, but I think it makes sense to do it per-voice given TripleOscillator's modulation options.
  • Phase randomization is essential to create a supersaw sound. Creating such a sound in 3xosc is a simple matter of layering, while in TripleOscillator it is basically impossible. You can fake it, but it isn't the same.

image

There are two things to note with this PR:

  1. The art needs reworking, preferably by whoever made the original. I don't have the project files for it and there's only so much you can do with a clone tool.
  2. I'm not entirely sure what side effects the change in Oscillator.h will have, if any. Using a float reference meant I couldn't pass in the result of an addition to the object without things becoming very broken very quickly.

Tagging @curlymorphic.

// setup phase randomization knob
Knob * phrk = new TripleOscKnob( this );
phrk->move( 165, knob_y );
phrk->setHintText( tr( "Osc %1 phase rand:" ).
Copy link
Member

Choose a reason for hiding this comment

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

How about "Osc %1 phase randomization:"? Don't want to throw off new users with too much jargon.

Copy link
Member Author

Choose a reason for hiding this comment

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

Works for me. I'll update that later today.

float phaseRandR;

phaseRandL = fastRandf( 1 ) * m_osc[i]->m_phaseRand;
phaseRandR = fastRandf( 1 ) * m_osc[i]->m_phaseRand;
Copy link
Member

Choose a reason for hiding this comment

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

Any reason for separating the declaration and initialization of these? Why not just


			float phaseRandL = fastRandf( 1 ) * m_osc[i]->m_phaseRand;
			float phaseRandR = fastRandf( 1 ) * m_osc[i]->m_phaseRand; 

?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually no, thanks for pointing that out. It made sense when it was set conditionally, but I removed that.

@curlymorphic
Copy link
Contributor

I am liking the sound of this.
I have had a look at the code, and it appears that your change to the OSCILLATOR should not have any negative impacts, as both m_ext_phaseOffset and m_phaseOffset are initialized to the same variable, prior to this pull request m_ext_phaseOffset appears to be unused. However a test with all the internal synths that use this oscillator would be beneficial.

@SecondFlight
Copy link
Member Author

Alright @curlymorphic, thanks for taking the time to review. I'll merge the PR once I've tested everything and @LocoMatt has gotten back to me with the updated artwork.

@@ -306,6 +324,9 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
for( int i = NUM_OF_OSCILLATORS - 1; i >= 0; --i )
{

float phaseRandL = fastRandf( 1 ) * m_osc[i]->m_phaseRand;
float phaseRandR = fastRandf( 1 ) * m_osc[i]->m_phaseRand;
Copy link
Member

Choose a reason for hiding this comment

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

FYI, a periods corresponds to 2π in phase.

Copy link
Member

Choose a reason for hiding this comment

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

Never mind, Oscillator uses unusual convention(a period = phase of 1). So this code is fine as-is.

Copy link
Member

Choose a reason for hiding this comment

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

Just a random thought: would it be better to use -0.5 ~ 0.5 instead of 0 ~ 1?

@@ -314,14 +335,14 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
&m_osc[i]->m_modulationAlgoModel,
_n->frequency(),
m_osc[i]->m_detuningLeft,
m_osc[i]->m_phaseOffsetLeft,
m_osc[i]->m_phaseOffsetLeft + phaseRandL,
Copy link
Member

Choose a reason for hiding this comment

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

Use a space before + instead of a tab.

@@ -331,22 +352,21 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
&m_osc[i]->m_modulationAlgoModel,
_n->frequency(),
m_osc[i]->m_detuningLeft,
m_osc[i]->m_phaseOffsetLeft,
m_osc[i]->m_phaseOffsetLeft + phaseRandL,
Copy link
Member

Choose a reason for hiding this comment

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

Here, too.

@PhysSong
Copy link
Member

prior to this pull request m_ext_phaseOffset appears to be unused.

No, this is needed by TripleOscillator. Currently one can change oscillator phase while playing a note. With this PR the behavior will be lost.

void OscillatorObject::updatePhaseOffsetLeft()
{
m_phaseOffsetLeft = ( m_phaseOffsetModel.value() +
m_stereoPhaseDetuningModel.value() ) / 360.0f;
}
void OscillatorObject::updatePhaseOffsetRight()
{
m_phaseOffsetRight = m_phaseOffsetModel.value() / 360.0f;
}

Also, there's no point to keep m_ext_phaseOffset if you change it from reference to normal variable. Please consider dropping m_ext_phaseOffset and find a way to replace Oscillator::recalcPhase().

@SecondFlight
Copy link
Member Author

Sounds good, thanks for looking into that! I remember thinking I'd go back to look at the live updating phase but I must have forgotten. I'll implement all those changes along with the UI as soon as I get the artwork.

@PhysSong
Copy link
Member

FYI, I'm planning to add pitch randomization and unison later. So I afraid PR in the artwork may be unclear(P can be either pitch or phase).

@SecondFlight
Copy link
Member Author

SecondFlight commented May 29, 2018

I was planning to do the same thing, so let me go ahead and share my thoughts about that.

I'm concerned about the UI getting cluttered. I'm not sure there's room for three more knobs per row. My thought was to add a unison feature in the chord / arpeggios tab.

Of course, you would save two knobs per row if you made unison a global feature instead of per-voice, and I think that could make a lot of sense.

Allowing for different sizes of plugins would also be a great way to solve this, but that's a whole different can of worms. For now though, the small size encourages simplicity which I don't think is a bad thing.

Anyways, those are my thoughts, I'll take that off my list of things to eventually do :)

@SecondFlight
Copy link
Member Author

Oh also, I'll let LocoMatt know to make the text different. He hasn't done the text yet, and I think I'll have him put "PHR".

@PhysSong
Copy link
Member

TODOs:

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.

4 participants