Audio processing with normalized [0.0, 1.0] domain











up vote
1
down vote

favorite












I've a code that process buffers of samples/audio data. Here's the code:



#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>

#define PI 3.141592653589793238
#define TWOPI 6.283185307179586476

const int blockSize = 256;
const double mSampleRate = 44100.0;
const double mHostPitch = 2.0;
const double mRadiansPerSample = TWOPI / mSampleRate;

double mGainNormalizedValues[blockSize];
double mPitchNormalizedValues[blockSize];
double mOffsetNormalizedValues[blockSize];

class Oscillator
{
public:
double mPhase = 0.0;
double minPitch = -48.0;
double maxPitch = 48.0;
double rangePitch = maxPitch - minPitch;
double pitchPd = log(2.0) / 12.0;
double minOffset = -900.0;
double maxOffset = 900.0;
double rangeOffset = maxOffset - minOffset;

Oscillator() { }

void ProcessBuffer(double voiceFrequency, int blockSize, double *left, double *right) {
// precomputed data
double bp0 = voiceFrequency * mHostPitch;

// process block
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
double output = (sin(mPhase)) * mGainNormalizedValues[sampleIndex];

*left++ += output;
*right++ += output;

// next phase
mPhase += std::clamp(mRadiansPerSample * (bp0 * WarpPitch(mPitchNormalizedValues[sampleIndex])) + WarpOffset(mOffsetNormalizedValues[sampleIndex]), 0.0, PI);
while (mPhase >= TWOPI) { mPhase -= TWOPI; }
}
}

inline double WarpPitch(double normalizedValue) { return exp((minPitch + normalizedValue * rangePitch) * pitchPd); }
inline double WarpOffset(double normalizedValue) { return minOffset + normalizedValue * rangeOffset; }
};

int main(int argc, const char *argv) {
int numBuffer = 1024;
int counterBuffer = 0;

Oscillator oscillator;

// I fill the buffer often
while (counterBuffer < numBuffer) {
// init buffer
double bufferLeft[blockSize];
double bufferRight[blockSize];
memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));

// emulate params values for this buffer
for(int i = 0; i < blockSize; i++) {
mGainNormalizedValues[i] = i / (double)blockSize;
mPitchNormalizedValues[i] = i / (double)blockSize;
mOffsetNormalizedValues[i] = i / (double)blockSize;
}

// process osc buffer
oscillator.ProcessBuffer(130.81278, blockSize, &bufferLeft[0], &bufferRight[0]);

// do somethings with buffer

counterBuffer++;
}
}


Basically:




  1. init a Oscillator object

  2. for each buffer, fill some param arrays with values (gain, pitch, offset); gain remain normalized [0.0, 1.0], while pitch and offset range in -48/48 and -900/900

  3. then I iterate the buffer, calculating the Oscillator's sine due to pitch and offset, and I apply a gain; later, I move the phase, incrementing it


The whole domain of operations are normalized [0.0, 1.0]. But when I need to manage pitch and offset, I need to switch domain and use different values (i.e. the Warp functions).



This required lots of computations and process. I'd like to avoid it, so I can improve the code and performances.



How would you do it? Can I keep into [0.0, 1.0]? Could I improve the performance?










share|improve this question
























  • Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
    – harold
    Nov 13 at 15:57










  • @harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
    – markzzz
    Nov 13 at 15:59

















up vote
1
down vote

favorite












I've a code that process buffers of samples/audio data. Here's the code:



#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>

#define PI 3.141592653589793238
#define TWOPI 6.283185307179586476

const int blockSize = 256;
const double mSampleRate = 44100.0;
const double mHostPitch = 2.0;
const double mRadiansPerSample = TWOPI / mSampleRate;

double mGainNormalizedValues[blockSize];
double mPitchNormalizedValues[blockSize];
double mOffsetNormalizedValues[blockSize];

class Oscillator
{
public:
double mPhase = 0.0;
double minPitch = -48.0;
double maxPitch = 48.0;
double rangePitch = maxPitch - minPitch;
double pitchPd = log(2.0) / 12.0;
double minOffset = -900.0;
double maxOffset = 900.0;
double rangeOffset = maxOffset - minOffset;

Oscillator() { }

void ProcessBuffer(double voiceFrequency, int blockSize, double *left, double *right) {
// precomputed data
double bp0 = voiceFrequency * mHostPitch;

// process block
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
double output = (sin(mPhase)) * mGainNormalizedValues[sampleIndex];

*left++ += output;
*right++ += output;

// next phase
mPhase += std::clamp(mRadiansPerSample * (bp0 * WarpPitch(mPitchNormalizedValues[sampleIndex])) + WarpOffset(mOffsetNormalizedValues[sampleIndex]), 0.0, PI);
while (mPhase >= TWOPI) { mPhase -= TWOPI; }
}
}

inline double WarpPitch(double normalizedValue) { return exp((minPitch + normalizedValue * rangePitch) * pitchPd); }
inline double WarpOffset(double normalizedValue) { return minOffset + normalizedValue * rangeOffset; }
};

int main(int argc, const char *argv) {
int numBuffer = 1024;
int counterBuffer = 0;

Oscillator oscillator;

// I fill the buffer often
while (counterBuffer < numBuffer) {
// init buffer
double bufferLeft[blockSize];
double bufferRight[blockSize];
memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));

// emulate params values for this buffer
for(int i = 0; i < blockSize; i++) {
mGainNormalizedValues[i] = i / (double)blockSize;
mPitchNormalizedValues[i] = i / (double)blockSize;
mOffsetNormalizedValues[i] = i / (double)blockSize;
}

// process osc buffer
oscillator.ProcessBuffer(130.81278, blockSize, &bufferLeft[0], &bufferRight[0]);

// do somethings with buffer

counterBuffer++;
}
}


Basically:




  1. init a Oscillator object

  2. for each buffer, fill some param arrays with values (gain, pitch, offset); gain remain normalized [0.0, 1.0], while pitch and offset range in -48/48 and -900/900

  3. then I iterate the buffer, calculating the Oscillator's sine due to pitch and offset, and I apply a gain; later, I move the phase, incrementing it


The whole domain of operations are normalized [0.0, 1.0]. But when I need to manage pitch and offset, I need to switch domain and use different values (i.e. the Warp functions).



This required lots of computations and process. I'd like to avoid it, so I can improve the code and performances.



How would you do it? Can I keep into [0.0, 1.0]? Could I improve the performance?










share|improve this question
























  • Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
    – harold
    Nov 13 at 15:57










  • @harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
    – markzzz
    Nov 13 at 15:59















up vote
1
down vote

favorite









up vote
1
down vote

favorite











I've a code that process buffers of samples/audio data. Here's the code:



#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>

#define PI 3.141592653589793238
#define TWOPI 6.283185307179586476

const int blockSize = 256;
const double mSampleRate = 44100.0;
const double mHostPitch = 2.0;
const double mRadiansPerSample = TWOPI / mSampleRate;

double mGainNormalizedValues[blockSize];
double mPitchNormalizedValues[blockSize];
double mOffsetNormalizedValues[blockSize];

class Oscillator
{
public:
double mPhase = 0.0;
double minPitch = -48.0;
double maxPitch = 48.0;
double rangePitch = maxPitch - minPitch;
double pitchPd = log(2.0) / 12.0;
double minOffset = -900.0;
double maxOffset = 900.0;
double rangeOffset = maxOffset - minOffset;

Oscillator() { }

void ProcessBuffer(double voiceFrequency, int blockSize, double *left, double *right) {
// precomputed data
double bp0 = voiceFrequency * mHostPitch;

// process block
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
double output = (sin(mPhase)) * mGainNormalizedValues[sampleIndex];

*left++ += output;
*right++ += output;

// next phase
mPhase += std::clamp(mRadiansPerSample * (bp0 * WarpPitch(mPitchNormalizedValues[sampleIndex])) + WarpOffset(mOffsetNormalizedValues[sampleIndex]), 0.0, PI);
while (mPhase >= TWOPI) { mPhase -= TWOPI; }
}
}

inline double WarpPitch(double normalizedValue) { return exp((minPitch + normalizedValue * rangePitch) * pitchPd); }
inline double WarpOffset(double normalizedValue) { return minOffset + normalizedValue * rangeOffset; }
};

int main(int argc, const char *argv) {
int numBuffer = 1024;
int counterBuffer = 0;

Oscillator oscillator;

// I fill the buffer often
while (counterBuffer < numBuffer) {
// init buffer
double bufferLeft[blockSize];
double bufferRight[blockSize];
memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));

// emulate params values for this buffer
for(int i = 0; i < blockSize; i++) {
mGainNormalizedValues[i] = i / (double)blockSize;
mPitchNormalizedValues[i] = i / (double)blockSize;
mOffsetNormalizedValues[i] = i / (double)blockSize;
}

// process osc buffer
oscillator.ProcessBuffer(130.81278, blockSize, &bufferLeft[0], &bufferRight[0]);

// do somethings with buffer

counterBuffer++;
}
}


Basically:




  1. init a Oscillator object

  2. for each buffer, fill some param arrays with values (gain, pitch, offset); gain remain normalized [0.0, 1.0], while pitch and offset range in -48/48 and -900/900

  3. then I iterate the buffer, calculating the Oscillator's sine due to pitch and offset, and I apply a gain; later, I move the phase, incrementing it


The whole domain of operations are normalized [0.0, 1.0]. But when I need to manage pitch and offset, I need to switch domain and use different values (i.e. the Warp functions).



This required lots of computations and process. I'd like to avoid it, so I can improve the code and performances.



How would you do it? Can I keep into [0.0, 1.0]? Could I improve the performance?










share|improve this question















I've a code that process buffers of samples/audio data. Here's the code:



#include <iostream>
#include <math.h>
#include <cstring>
#include <algorithm>

#define PI 3.141592653589793238
#define TWOPI 6.283185307179586476

const int blockSize = 256;
const double mSampleRate = 44100.0;
const double mHostPitch = 2.0;
const double mRadiansPerSample = TWOPI / mSampleRate;

double mGainNormalizedValues[blockSize];
double mPitchNormalizedValues[blockSize];
double mOffsetNormalizedValues[blockSize];

class Oscillator
{
public:
double mPhase = 0.0;
double minPitch = -48.0;
double maxPitch = 48.0;
double rangePitch = maxPitch - minPitch;
double pitchPd = log(2.0) / 12.0;
double minOffset = -900.0;
double maxOffset = 900.0;
double rangeOffset = maxOffset - minOffset;

Oscillator() { }

void ProcessBuffer(double voiceFrequency, int blockSize, double *left, double *right) {
// precomputed data
double bp0 = voiceFrequency * mHostPitch;

// process block
for (int sampleIndex = 0; sampleIndex < blockSize; sampleIndex++) {
double output = (sin(mPhase)) * mGainNormalizedValues[sampleIndex];

*left++ += output;
*right++ += output;

// next phase
mPhase += std::clamp(mRadiansPerSample * (bp0 * WarpPitch(mPitchNormalizedValues[sampleIndex])) + WarpOffset(mOffsetNormalizedValues[sampleIndex]), 0.0, PI);
while (mPhase >= TWOPI) { mPhase -= TWOPI; }
}
}

inline double WarpPitch(double normalizedValue) { return exp((minPitch + normalizedValue * rangePitch) * pitchPd); }
inline double WarpOffset(double normalizedValue) { return minOffset + normalizedValue * rangeOffset; }
};

int main(int argc, const char *argv) {
int numBuffer = 1024;
int counterBuffer = 0;

Oscillator oscillator;

// I fill the buffer often
while (counterBuffer < numBuffer) {
// init buffer
double bufferLeft[blockSize];
double bufferRight[blockSize];
memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));

// emulate params values for this buffer
for(int i = 0; i < blockSize; i++) {
mGainNormalizedValues[i] = i / (double)blockSize;
mPitchNormalizedValues[i] = i / (double)blockSize;
mOffsetNormalizedValues[i] = i / (double)blockSize;
}

// process osc buffer
oscillator.ProcessBuffer(130.81278, blockSize, &bufferLeft[0], &bufferRight[0]);

// do somethings with buffer

counterBuffer++;
}
}


Basically:




  1. init a Oscillator object

  2. for each buffer, fill some param arrays with values (gain, pitch, offset); gain remain normalized [0.0, 1.0], while pitch and offset range in -48/48 and -900/900

  3. then I iterate the buffer, calculating the Oscillator's sine due to pitch and offset, and I apply a gain; later, I move the phase, incrementing it


The whole domain of operations are normalized [0.0, 1.0]. But when I need to manage pitch and offset, I need to switch domain and use different values (i.e. the Warp functions).



This required lots of computations and process. I'd like to avoid it, so I can improve the code and performances.



How would you do it? Can I keep into [0.0, 1.0]? Could I improve the performance?







c++ performance signal-processing






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 13 at 20:24









200_success

127k15148410




127k15148410










asked Nov 13 at 11:18









markzzz

1254




1254












  • Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
    – harold
    Nov 13 at 15:57










  • @harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
    – markzzz
    Nov 13 at 15:59




















  • Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
    – harold
    Nov 13 at 15:57










  • @harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
    – markzzz
    Nov 13 at 15:59


















Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
– harold
Nov 13 at 15:57




Does it make sense to add optimization that rely on the phase offset per step to be stable for a while, or is it always expected to fluctuate on a sample by sample basis?
– harold
Nov 13 at 15:57












@harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
– markzzz
Nov 13 at 15:59






@harold: I could control rate it and save on cycles, but let consider the worst case; so yes, its always expected to fluctuate sample by sample. But feel free to give an example that can be stable for a while, it would still be interessant.
– markzzz
Nov 13 at 15:59












2 Answers
2






active

oldest

votes

















up vote
3
down vote













Prefer C++ headers



Instead of <math.h>, it's better to include <cmath> and qualify names such as std::log.



Prefer constants to macros



Re-write pi as a strongly-typed, scoped variable rather than a preprocessor macro. Same for 2*pi if you really feel the need.



Manage line lengths



Some lines are very long. In many cases, they just need newlines adding (e.g. bodies of inline functions can have their own lines).



In the case of the std::clamp() call, it's probably worth using variables to give a name to the candidate value before clamping.



Easier sizeof



Instead of recomputing the size of bufferLeft and bufferRight like this:




    memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));



It's easier and clearer to just use the whole array size:



    memset(&bufferLeft, 0, sizeof bufferLeft);
memset(&bufferRight, 0, sizeof bufferRight);


Personally, I'd generally prefer std::fill to match types and ensure the intention is clear:



    std::fill(std::begin(bufferLeft), std::end(bufferLeft), 0.0);
std::fill(std::begin(bufferRight), std::end(bufferRight), 0.0);


Then I don't need to think about whether all-bits zero is the same as 0.0 or not.






share|improve this answer





















  • Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
    – markzzz
    Nov 13 at 14:24


















up vote
1
down vote













This is the "constant frequency" optimization mentioned in the comments, it is of course situational..



Sine and cosine are what happens to coordinates of a unit vector that is rotated around the origin, which means that a sequence like sin(start + k*rate) can be generated by starting a unit vector at the starting point [cos(start), sin(start)] and then successively multiplying it by the rotation matrix



[[cos(rate), -sin(rate)],
[sin(rate), cos(rate)]]


To generate each of the values, with the result being the Y coordinate of the resulting vectors.



So for a stretch of audio in which the frequency is not itself warped (amplitude can be varied on top of this though and that ends up causing some frequency-spread of its own), only a pair of sine and cosine are needed, the rest happens with multiplication and addition. But of course this does not help if the frequency does change all the time.






share|improve this answer





















  • I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
    – markzzz
    Nov 13 at 20:52











Your Answer





StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");

StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207547%2faudio-processing-with-normalized-0-0-1-0-domain%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
3
down vote













Prefer C++ headers



Instead of <math.h>, it's better to include <cmath> and qualify names such as std::log.



Prefer constants to macros



Re-write pi as a strongly-typed, scoped variable rather than a preprocessor macro. Same for 2*pi if you really feel the need.



Manage line lengths



Some lines are very long. In many cases, they just need newlines adding (e.g. bodies of inline functions can have their own lines).



In the case of the std::clamp() call, it's probably worth using variables to give a name to the candidate value before clamping.



Easier sizeof



Instead of recomputing the size of bufferLeft and bufferRight like this:




    memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));



It's easier and clearer to just use the whole array size:



    memset(&bufferLeft, 0, sizeof bufferLeft);
memset(&bufferRight, 0, sizeof bufferRight);


Personally, I'd generally prefer std::fill to match types and ensure the intention is clear:



    std::fill(std::begin(bufferLeft), std::end(bufferLeft), 0.0);
std::fill(std::begin(bufferRight), std::end(bufferRight), 0.0);


Then I don't need to think about whether all-bits zero is the same as 0.0 or not.






share|improve this answer





















  • Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
    – markzzz
    Nov 13 at 14:24















up vote
3
down vote













Prefer C++ headers



Instead of <math.h>, it's better to include <cmath> and qualify names such as std::log.



Prefer constants to macros



Re-write pi as a strongly-typed, scoped variable rather than a preprocessor macro. Same for 2*pi if you really feel the need.



Manage line lengths



Some lines are very long. In many cases, they just need newlines adding (e.g. bodies of inline functions can have their own lines).



In the case of the std::clamp() call, it's probably worth using variables to give a name to the candidate value before clamping.



Easier sizeof



Instead of recomputing the size of bufferLeft and bufferRight like this:




    memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));



It's easier and clearer to just use the whole array size:



    memset(&bufferLeft, 0, sizeof bufferLeft);
memset(&bufferRight, 0, sizeof bufferRight);


Personally, I'd generally prefer std::fill to match types and ensure the intention is clear:



    std::fill(std::begin(bufferLeft), std::end(bufferLeft), 0.0);
std::fill(std::begin(bufferRight), std::end(bufferRight), 0.0);


Then I don't need to think about whether all-bits zero is the same as 0.0 or not.






share|improve this answer





















  • Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
    – markzzz
    Nov 13 at 14:24













up vote
3
down vote










up vote
3
down vote









Prefer C++ headers



Instead of <math.h>, it's better to include <cmath> and qualify names such as std::log.



Prefer constants to macros



Re-write pi as a strongly-typed, scoped variable rather than a preprocessor macro. Same for 2*pi if you really feel the need.



Manage line lengths



Some lines are very long. In many cases, they just need newlines adding (e.g. bodies of inline functions can have their own lines).



In the case of the std::clamp() call, it's probably worth using variables to give a name to the candidate value before clamping.



Easier sizeof



Instead of recomputing the size of bufferLeft and bufferRight like this:




    memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));



It's easier and clearer to just use the whole array size:



    memset(&bufferLeft, 0, sizeof bufferLeft);
memset(&bufferRight, 0, sizeof bufferRight);


Personally, I'd generally prefer std::fill to match types and ensure the intention is clear:



    std::fill(std::begin(bufferLeft), std::end(bufferLeft), 0.0);
std::fill(std::begin(bufferRight), std::end(bufferRight), 0.0);


Then I don't need to think about whether all-bits zero is the same as 0.0 or not.






share|improve this answer












Prefer C++ headers



Instead of <math.h>, it's better to include <cmath> and qualify names such as std::log.



Prefer constants to macros



Re-write pi as a strongly-typed, scoped variable rather than a preprocessor macro. Same for 2*pi if you really feel the need.



Manage line lengths



Some lines are very long. In many cases, they just need newlines adding (e.g. bodies of inline functions can have their own lines).



In the case of the std::clamp() call, it's probably worth using variables to give a name to the candidate value before clamping.



Easier sizeof



Instead of recomputing the size of bufferLeft and bufferRight like this:




    memset(&bufferLeft, 0, blockSize * sizeof(double));
memset(&bufferRight, 0, blockSize * sizeof(double));



It's easier and clearer to just use the whole array size:



    memset(&bufferLeft, 0, sizeof bufferLeft);
memset(&bufferRight, 0, sizeof bufferRight);


Personally, I'd generally prefer std::fill to match types and ensure the intention is clear:



    std::fill(std::begin(bufferLeft), std::end(bufferLeft), 0.0);
std::fill(std::begin(bufferRight), std::end(bufferRight), 0.0);


Then I don't need to think about whether all-bits zero is the same as 0.0 or not.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 13 at 14:07









Toby Speight

21.9k536107




21.9k536107












  • Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
    – markzzz
    Nov 13 at 14:24


















  • Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
    – markzzz
    Nov 13 at 14:24
















Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
– markzzz
Nov 13 at 14:24




Thanks for all tips :) I'll adapt them! What about math/sine/normalized? :D That's the core of the question...
– markzzz
Nov 13 at 14:24












up vote
1
down vote













This is the "constant frequency" optimization mentioned in the comments, it is of course situational..



Sine and cosine are what happens to coordinates of a unit vector that is rotated around the origin, which means that a sequence like sin(start + k*rate) can be generated by starting a unit vector at the starting point [cos(start), sin(start)] and then successively multiplying it by the rotation matrix



[[cos(rate), -sin(rate)],
[sin(rate), cos(rate)]]


To generate each of the values, with the result being the Y coordinate of the resulting vectors.



So for a stretch of audio in which the frequency is not itself warped (amplitude can be varied on top of this though and that ends up causing some frequency-spread of its own), only a pair of sine and cosine are needed, the rest happens with multiplication and addition. But of course this does not help if the frequency does change all the time.






share|improve this answer





















  • I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
    – markzzz
    Nov 13 at 20:52















up vote
1
down vote













This is the "constant frequency" optimization mentioned in the comments, it is of course situational..



Sine and cosine are what happens to coordinates of a unit vector that is rotated around the origin, which means that a sequence like sin(start + k*rate) can be generated by starting a unit vector at the starting point [cos(start), sin(start)] and then successively multiplying it by the rotation matrix



[[cos(rate), -sin(rate)],
[sin(rate), cos(rate)]]


To generate each of the values, with the result being the Y coordinate of the resulting vectors.



So for a stretch of audio in which the frequency is not itself warped (amplitude can be varied on top of this though and that ends up causing some frequency-spread of its own), only a pair of sine and cosine are needed, the rest happens with multiplication and addition. But of course this does not help if the frequency does change all the time.






share|improve this answer





















  • I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
    – markzzz
    Nov 13 at 20:52













up vote
1
down vote










up vote
1
down vote









This is the "constant frequency" optimization mentioned in the comments, it is of course situational..



Sine and cosine are what happens to coordinates of a unit vector that is rotated around the origin, which means that a sequence like sin(start + k*rate) can be generated by starting a unit vector at the starting point [cos(start), sin(start)] and then successively multiplying it by the rotation matrix



[[cos(rate), -sin(rate)],
[sin(rate), cos(rate)]]


To generate each of the values, with the result being the Y coordinate of the resulting vectors.



So for a stretch of audio in which the frequency is not itself warped (amplitude can be varied on top of this though and that ends up causing some frequency-spread of its own), only a pair of sine and cosine are needed, the rest happens with multiplication and addition. But of course this does not help if the frequency does change all the time.






share|improve this answer












This is the "constant frequency" optimization mentioned in the comments, it is of course situational..



Sine and cosine are what happens to coordinates of a unit vector that is rotated around the origin, which means that a sequence like sin(start + k*rate) can be generated by starting a unit vector at the starting point [cos(start), sin(start)] and then successively multiplying it by the rotation matrix



[[cos(rate), -sin(rate)],
[sin(rate), cos(rate)]]


To generate each of the values, with the result being the Y coordinate of the resulting vectors.



So for a stretch of audio in which the frequency is not itself warped (amplitude can be varied on top of this though and that ends up causing some frequency-spread of its own), only a pair of sine and cosine are needed, the rest happens with multiplication and addition. But of course this does not help if the frequency does change all the time.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 13 at 16:20









harold

98357




98357












  • I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
    – markzzz
    Nov 13 at 20:52


















  • I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
    – markzzz
    Nov 13 at 20:52
















I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
– markzzz
Nov 13 at 20:52




I see, thanks. But Yes, frequency/pitch/gain change constantly (or control rated)
– markzzz
Nov 13 at 20:52


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207547%2faudio-processing-with-normalized-0-0-1-0-domain%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Список кардиналов, возведённых папой римским Каликстом III

Deduzione

Mysql.sock missing - “Can't connect to local MySQL server through socket”