Problems when multiplying sine waves

Hello,

I am new to Cinder and am trying to use it to write synthesized audio and visuals that can be influenced in realtime.

I started by trying to write a simple fm synth. Two Sine Oscillators, that get multiplied and then the result as well as the individual sine waves gets send to the output:

#include "cinder/app/App.h"
#include "cinder/app/RendererGl.h"
#include "cinder/gl/gl.h"

#include "cinder/audio/Voice.h"
#include "cinder/audio/Source.h"
#include "cinder/audio/Context.h"
#include "cinder/audio/GenNode.h"
#include "cinder/audio/GainNode.h"


#include "Resources.h"

using namespace ci;
using namespace ci::app;
using namespace std;



class test_01App : public App {
  public:
    void setup() override;
	void draw() override;
    
    audio::GenNodeRef mSine;
    audio::GenNodeRef mSine2;
    audio::GainNodeRef mGain;
    audio::GainNodeRef mGain2;
    audio::MultiplyNodeRef mMult;
};

void test_01App::setup()
{

    auto ctx = audio::Context::master();
    mSine = ctx->makeNode( new audio::GenSineNode );
    mSine2 = ctx->makeNode( new audio::GenSineNode );
    mMult = ctx->makeNode(new audio::MultiplyNode );

    mGain = ctx->makeNode( new audio::GainNode );
    mGain2 = ctx->makeNode( new audio::GainNode );

    mSine->setFreq(220);
    mSine2->setFreq(330);
    mGain->setValue(0.1f);
    mGain2->setValue(0.01f);

    mMult -> getParam() -> setProcessor(mSine2);
    mSine >> mMult;

    mSine >> mGain;
    mSine2 >> mGain;
    
    mMult >> mGain2;
    
    mGain >> ctx->getOutput();
    mGain2 >> ctx->getOutput();

    
    mSine->enable();
    mSine2->enable();
    
    ctx->enable();

    
}

However, I get a horribly distorted sound. If I only send mSine and the result of mMult to the output, the sound is fine. Why is mSine2 distorting if it gets used as a parameter for the mMult, but not if it doesn’t? Does the operator output get downsampled to be used as a control parameter?

Also, the sound of the mMult result on its own differs significantly from the sound of multiplying two sine waves of these frequencies in pure data. Why is that?

I would be very happy, if you could help me with these problems.

Hi there,

I’m not sure if you node setup is what you intended.
If you do

    mSine >> mGain;
    mSine2 >> mGain;

Wouldn’t mSine2 overwrite your mSine output? You could double check by calling

audio::Context::master()->printGraphToString()

to make sure your node setup is as intended.

-seph

Hi seih,

thanks for your reply.

the output of the Log is:

-- cinder::audio::GainNode	[ enabled, ch: 1, ch mode: matches input, sum ]
-- -- cinder::audio::GenSineNode	[ enabled, ch: 1, ch mode: specified, sum ]
-- -- cinder::audio::GenSineNode	[ enabled, ch: 1, ch mode: specified, in-place ]
-- cinder::audio::GainNode	[ enabled, ch: 1, ch mode: matches input, sum ]
-- -- cinder::audio::MultiplyNode	[ enabled, ch: 1, ch mode: matches input, in-place ]
-- -- -- cinder::audio::GenSineNode	[ ** already printed ** ]

So from what I understand, mGain cleanly sums the two channels together, which is what I expected and heard when commenting only using:

    auto ctx = audio::Context::master();

    mSine = ctx->makeNode( new audio::GenSineNode );
    mSine2 = ctx->makeNode( new audio::GenSineNode );

    mGain = ctx->makeNode( new audio::GainNode );
    mGain2 = ctx->makeNode( new audio::GainNode );

    mSine->setFreq(220);
    mSine2->setFreq(330);
    mGain->setValue(0.1f);
    mGain2->setValue(0.01f);

    mSine >> mGain;
    mSine2 >> mGain;
        
    mGain >> ctx->getOutput();

    mSine->enable();
    mSine2->enable();
    
    CI_LOG_D(audio::Context::master()->printGraphToString());
    
    ctx->enable();

If I use the original code, but comment out sending the original sine waves to their own gain, I can hear the modulated sine wave (though audio::MultiplyNode seems to turn down the Modulator in Amplitude, or that happens due to getParam() → setProcessor()):

void test_01App::setup()
{

    auto ctx = audio::Context::master();
    mSine = ctx->makeNode( new audio::GenSineNode );
    mSine2 = ctx->makeNode( new audio::GenSineNode );
    mMult = ctx->makeNode(new audio::MultiplyNode );

    mGain = ctx->makeNode( new audio::GainNode );
    mGain2 = ctx->makeNode( new audio::GainNode );

    mSine->setFreq(220);
    mSine2->setFreq(330);
    mGain->setValue(0.1f);
    mGain2->setValue(0.01f);
    mMult -> getParam() -> setProcessor(mSine2);
    
    mSine >> mMult;
    
//    mSine >> mGain;
//    mSine2 >> mGain;
    
    mMult >> mGain2;
    
    mGain >> ctx->getOutput();
    mGain2 >> ctx->getOutput();

    
    mSine->enable();
    mSine2->enable();
    
    CI_LOG_D(audio::Context::master()->printGraphToString());
    
    ctx->enable();

    
}

The resulting Log is:

-- cinder::audio::GainNode	[ enabled, ch: 1, ch mode: matches input, sum ]
-- -- cinder::audio::MultiplyNode	[ enabled, ch: 1, ch mode: matches input, in-place ]
-- -- -- cinder::audio::GenSineNode	[ enabled, ch: 1, ch mode: specified, in-place ]

and finally the original code results in a distorted Sine2 with this Log:

-- cinder::audio::GainNode	[ enabled, ch: 1, ch mode: matches input, sum ]
-- -- cinder::audio::GenSineNode	[ enabled, ch: 1, ch mode: specified, sum ]
-- -- cinder::audio::GenSineNode	[ enabled, ch: 1, ch mode: specified, in-place ]
-- cinder::audio::GainNode	[ enabled, ch: 1, ch mode: matches input, sum ]
-- -- cinder::audio::MultiplyNode	[ enabled, ch: 1, ch mode: matches input, in-place ]
-- -- -- cinder::audio::GenSineNode	[ ** already printed ** ]

So I don’t think, sending the two sines to the same Gain is the Problem.

I’ve just tried your original code, it sounds alright for me, but I’m not sure what it supposed to be pure data.

If the error happens due to adding the multiply node, I have a wild guess:
run your code in Release mode will fix it.

I just tried running my code in release mode. It results in a failed build, because for some reason Xcode is “building for macOS-arm64 but attempting to link with file built for macOS-x86_64” and therefore ignoring the cinder library. I have chosen my Macbook as destination, which is intel architecture, not arm. I have no idea why Xcode thinks otherwise…

you may need to set “Build Active Architecture Only” to Yes under Build Settings

Down down-sampling in audio::Params, they are full audio rate controllers.

Another way to debug what is going wrong in your audio graph is to visualize the output with one of the MonitorNodes.

I haven’t studied you graph too closely, but is it intentional that mSine is being fed to both mMult and mGain ? I’m not understanding that part.

I was able to build it in release mode, but the problem did not go away there.

I am intentionally sending mSine into mMult and mGain, because I want the frequencies resulting from the multiplied Signal, as well as the original sine waves.

I updated the code to make it a little more clear whats goint on and added comments to show, how when exactly it’s distorting:

void test_01App::setup()
{

    auto ctx = audio::Context::master();
    
    //init monitors
    mMonitorSines = ctx->makeNode(new audio::MonitorNode);
    mMonitorMult = ctx->makeNode(new audio::MonitorNode);
    mMonitorMaster = ctx->makeNode(new audio::MonitorNode);
    
    //init oscillators and mult
    mSine1 = ctx->makeNode( new audio::GenSineNode );
    mSine2 = ctx->makeNode( new audio::GenSineNode );
    mMult = ctx->makeNode(new audio::MultiplyNode );

    //init gains
    mGainSines = ctx->makeNode( new audio::GainNode );
    mGainMult = ctx->makeNode( new audio::GainNode );
    mMaster = ctx->makeNode(new audio::GainNode);

    //set frequs and gain values
    mSine1->setFreq(220);
    mSine2->setFreq(330);
    mGainSines->setValue(0.5f); //if I set this to 0.0f, the distortion stays
    mGainMult->setValue(0.5f); //if I set this to 0.0f, the distortion stays
    mMaster->setValue(0.5f); //if I set this to 0.0f, everything is silent (ofcourse)
    
    //multiplying oscillators
    mSine1 >> mMult;
    mMult -> getParam() -> setProcessor(mSine2);
    
    //gainstage sines, if I comment out the two following line, the distortion goes away
    mSine1 >> mGainSines; //if I comment out only this line, the distortion stays
    mSine2 >> mGainSines; //if I comment out only this line, the distortion goes away
    
    //gainstage mult
    mMult >> mGainMult; //if I comment out this line, the distortion goes away
    
    //gainstage master
    mGainSines >> mMaster; //if I comment out this line, the distortion stays
    mGainMult >> mMaster; //if I comment out this line, the distortion stays
    
    //master to output
    mMaster >> ctx->getOutput();

    //connecting monitors for all gain stages
    mGainSines >> mMonitorSines;
    mGainMult >> mMonitorMult;
    mMaster >> mMonitorMaster;
    
    //enabling oscillators
    mSine1->enable();
    mSine2->enable();
    
    CI_LOG_D(audio::Context::master()->printGraphToString());
    
    ctx->enable();

    
}

void test_01App::draw()
{
	gl::clear(Color(0, 0, 0));
    
    if( mMonitorMaster && mMonitorMaster->getNumConnectedInputs() ) {
            Rectf scopeRectMaster( getWindowWidth() - 210, 10, getWindowWidth() - 10, 110 );
            drawAudioBuffer( mMonitorMaster->getBuffer(), scopeRectMaster, true );
        }
    if( mMonitorSines && mMonitorSines->getNumConnectedInputs() ) {
            Rectf scopeRectSines( getWindowWidth() - 210, 120, getWindowWidth() - 10, 220 );
            drawAudioBuffer( mMonitorSines->getBuffer(), scopeRectSines, true );
        }
    if( mMonitorMult && mMonitorMult->getNumConnectedInputs() ) {
            Rectf scopeRectMult( getWindowWidth() - 210, 230, getWindowWidth() - 10, 330 );
            drawAudioBuffer( mMonitorMult->getBuffer(), scopeRectMult, true );
        }

}
CINDER_APP( test_01App, RendererGl )

I’ve also made screenshots of the monitors (klick to follow the link to imgur, in each picture from top to bottom: mMaster, mSines, mMult, i was not allwed to embed them here):

It does not seem to be the actual summing of the channels, that’s causeing the distortion, since setting mGainMult or mGainSine to 0.0f does not do anything.