Old 05-13-2014, 11:15 AM   #1
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default How to use IReaperPitchShift properly?

I must be using the pitch shifter object wrong since I can't make it behave like it does in Reaper itself, particularly with regards to modulating/automating the parameters. (It might be the wrong assumption on my part Reaper itself uses IReaperPitchShift for the per-take pitch envelopes for example, though...) The parameter changes are missed or come too late in the audio output in my extension plugin. (Particularly when using the elastique modes.) I've tried several variations of buffering/queueing the audio data, order of API calls etc, so I won't bother putting the current code here, it doesn't work, none of the other variations I've tried works. I would rather like to know what is the correct way to use pitch shifter object.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 05-13-2014 at 11:21 AM.
Xenakios is offline   Reply With Quote
Old 05-16-2014, 07:22 AM   #2
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 15,721
Default

REAPER internally does use the same interface you are provided -- it will get output of the pitch shifter as needed, and add a blocksize's worth of samples when there is insufficient output available. And repeat. The parameter changes are done before requesting a given block's output.

Might be good to make sure you're using elastique and not the builtin LQ modes...

Last edited by Justin; 05-16-2014 at 07:34 AM.
Justin is offline   Reply With Quote
Old 05-16-2014, 08:11 AM   #3
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Justin View Post
REAPER internally does use the same interface you are provided -- it will get output of the pitch shifter as needed, and add a blocksize's worth of samples when there is insufficient output available. And repeat. The parameter changes are done before requesting a given block's output.

Might be good to make sure you're using elastique and not the builtin LQ modes...
So Reaper isn't doing anything special when for example the source audio to be processed is short? (Like 0.5 seconds...) I've tried with the way you suggested in IRC some time back, but that didn't help in getting parameters changes to take effect close to the beginning/at the expected position of a short audio segment. These problems appear with the Soundtouch and Elastique modes. The low quality windowed processor does work as expected...If I set up a similar take pitch envelope in Reaper itself, the envelopes do play back as expected, also with Elastique.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-16-2014, 01:16 PM   #4
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 15,721
Default

Quote:
Originally Posted by Xenakios View Post
So Reaper isn't doing anything special when for example the source audio to be processed is short? (Like 0.5 seconds...) I've tried with the way you suggested in IRC some time back, but that didn't help in getting parameters changes to take effect close to the beginning/at the expected position of a short audio segment. These problems appear with the Soundtouch and Elastique modes. The low quality windowed processor does work as expected...If I set up a similar take pitch envelope in Reaper itself, the envelopes do play back as expected, also with Elastique.
The tweaks for short content happen within the elastique implementation of IReaperPitchShift. There's probably some mistake in your implementation, maybe you should post sample code?
Justin is offline   Reply With Quote
Old 05-19-2014, 09:09 AM   #5
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Justin View Post
The tweaks for short content happen within the elastique implementation of IReaperPitchShift. There's probably some mistake in your implementation, maybe you should post sample code?
This is the implementation I have that is built into the public build of the plugin (I know it has all kinds of unnecessary bufferings etc in it, but since the code runs ok'ish except for the corner case problems I've found, I have lived with it so far) :

Code:
while (m_fq.Available()<sizeof(double)*SamplesToMake*m_nch)
    {
        double timetoInterp=(1.0/m_source_len)*(m_TimeCounter-m_TakeOffset);
        double envoffset=m_envelopes[0].get_play_offset();
        double pitch_amount=minpitch+PitchRange*m_envelopes[0].GetInterpolatedNodeValue(timetoInterp+envoffset);
        pitch_amount=bound_value(minpitch,pitch_amount,maxpitch);
        double PitchRatio=(pow(2.0,pitch_amount/12.0));
        envoffset=m_envelopes[1].get_play_offset();
        double FormantPitch=minformant+FormantRange*m_envelopes[1].GetInterpolatedNodeValue(timetoInterp+envoffset);
        FormantPitch=bound_value(-12.0,FormantPitch,12.0);
        double FormantRatio=(pow(2.0,FormantPitch/12.0));
        envoffset=m_envelopes[2].get_play_offset();
        double interpolated_tempo=m_envelopes[2].GetInterpolatedNodeValue(timetoInterp+envoffset);
        double tempo_amount=m_envelopes[2].normalized_to_scaled_func(interpolated_tempo);
        tempo_amount=bound_value(mintempo,tempo_amount,maxtempo);
        m_shifter->set_tempo(tempo_amount);
        m_shifter->set_shift(PitchRatio);
        m_shifter->set_formant_shift(FormantRatio);
        int samples_to_fetch=granul;
        double* the_ShifterBuffer=m_shifter->GetBuffer(samples_to_fetch);
        input_transfer.time_s=m_TimeCounter;
        input_transfer.length=samples_to_fetch;
        if (m_InputSamplesBuf.size()<input_transfer.length*m_nch)
            m_InputSamplesBuf.resize(input_transfer.length*m_nch);
        input_transfer.samples=m_InputSamplesBuf.data();
        input_transfer.samplerate=outSamplerate;
        input_transfer.nch=m_nch;
        m_DiskInputSource->GetSamples(&input_transfer);
        for (int j=0;j<m_nch*samples_to_fetch;j++)
        {
            the_ShifterBuffer[j]=m_InputSamplesBuf[j];
        }
        m_shifter->BufferDone(granul);
        int wanted=granul*(1.0/tempo_amount);
        int n=m_shifter->GetSamples(wanted,m_OutputShifterBuffer.data());
        m_fq.Add((void*)m_OutputShifterBuffer.data(),n*sizeof(double)*m_nch);
        m_TimeCounter+=((double)samples_to_fetch/(double)outSamplerate);
    }
m_fq is WDL_FastQueue
m_DiskInputSource is a Reaper PCM_source
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 05-20-2014, 01:04 PM   #6
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 15,721
Default

(per IRC) Change to:
Code:
        double timetoInterp=(1.0/m_source_len)*(m_TimeCounter2-m_TakeOffset);
and add this line to the end:
Code:
        if (n) m_TimeCounter2+=((double)n/(double)outSamplerate);
Justin is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 02:39 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.