0
$\begingroup$

I have simple wave generator in PortAudio. I'm creating single sine wave, and then I use a lookup sine to play it. Now I've stucked here. I have no idea how I can add another sine sound. Is it possible to somehow add new channel with another sound?

Here is my code:

soundengine.cpp

#include "soundengine.h"
#include <QDebug>

void SoundEngine::run()
{
    PaStreamParameters outputParameters;
    int i;
    double t;
    turnOFF = false;
    isPressed = false;

    static unsigned long n=0;
    for( i=0; i<TABLE_SIZE; i++, n++ )
    {
        t = (double)i/(double)SAMPLE_RATE;
        data.sine[i] = 0.80f * (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
    }
    data.phase = 0;
    data.phase_increment = CalcPhaseIncrement(0);


    err = Pa_Initialize();
    if(err != paNoError) qDebug()<<"Błąd przy inicjalizacji strumienia:"<<Pa_GetErrorText(err);

    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
    if (outputParameters.device == paNoDevice) qDebug()<<"Błąd: Brak domyślnego urządzenia wyjścia!";

    outputParameters.channelCount = 2;       /* stereo output */
    outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
    outputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(
              &stream,
              NULL, /* no input */
              &outputParameters,
              SAMPLE_RATE,
              FRAMES_PER_BUFFER,
              paClipOff|paDitherOff,      /*paNoFlag we won't output out of range samples so don't bother clipping them */
              patestCallback,
              &data );
    if(err != paNoError) qDebug()<<"Błąd przy otwieraniu strumienia:"<<Pa_GetErrorText(err);
    err = Pa_StartStream( stream );
    if(err != paNoError) qDebug()<<"Błąd przy starcie strumienia:"<<Pa_GetErrorText(err);

    while (turnOFF == false) {
        Pa_Sleep(500);
    }

    err = Pa_StopStream( stream );
    if(err != paNoError) qDebug()<<"Błąd przy zatrzymywaniu strumienia:"<<Pa_GetErrorText(err);
    err = Pa_CloseStream( stream );
    if(err != paNoError) qDebug()<<"Błąd przy zamykaniu strumienia:"<<Pa_GetErrorText(err);
    Pa_Terminate();
}

float SoundEngine::LookupSine( paTestData *data, float phase )
{
    float fIndex = phase*TABLE_SIZE;
    int   index = (int) fIndex;
    float fract = fIndex - index;
    float lo = data->sine[index];
    float hi = data->sine[index+1];
    float val = lo + fract*(hi-lo);
    return val;
}

int SoundEngine::patestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
    paTestData *callData = (paTestData*)userData;
    float *out = (float*)outputBuffer;
    float sample;
    unsigned long i;

    (void) timeInfo; /* Prevent unused variable warnings. */
    (void) statusFlags;
    (void) inputBuffer;

    for( i=0; i<framesPerBuffer; i++ )
    {
            sample = LookupSine(callData, callData->phase);
            *out++ = sample;  /* left */
            *out++ = sample;  /* right */

        callData->phase += callData->phase_increment;
        if( callData->phase >= 1.0f ) callData->phase -= 1.0f;
    }

    return paContinue;
}

void SoundEngine::generateSine()
{
    if(isPressed == false)
    {
        data.phase_increment = CalcPhaseIncrement(220.0);
        isPressed = true;
    }


}

void SoundEngine::removeSine()
{
            data.phase_increment = CalcPhaseIncrement(0.0);
            isPressed = false;
}

To play sound I use generateSine() method. Maybe is there better way to play sound at given frequency, using one method(like generateSine() in my example). I would like to play two sounds by using generateSine() method twice.

$\endgroup$
  • 1
    $\begingroup$ Simply add the two waveforms together in time domain. Unless you are specifically asking about multi-channel usage in PA, then this is out of the scope. $\endgroup$ – jojek Dec 13 '17 at 12:42
  • $\begingroup$ but when I'll add another waveform to my actual waveform, and then change my phase_increment - it will change pitch of two waveforms. Some time ago I've done it without interpolation(lookup table), but with every sound there was noise at beginning and ending of sound becouse I didn't know how to fade it. Now I don't know what to do. $\endgroup$ – Lizard_Paszczyk Dec 13 '17 at 13:28
  • 1
    $\begingroup$ no, adding two tones doesn't change the pitch. You might be running into clipping, however, but you could solve that by scaling each tone with e.g. 0.5 prior to adding. $\endgroup$ – Marcus Müller Dec 13 '17 at 13:55
  • $\begingroup$ I didn't say adding tone change the pitch. Look at my example where I am control pitch by phase_increment. When I will have two waveforms added it will give me one waveform, that's pitch will be still controlled by phase_increment. $\endgroup$ – Lizard_Paszczyk Dec 13 '17 at 19:55
  • $\begingroup$ I don't understand that statement. You generate two tones with different phase increments. Then you add these two. That's the way to go. $\endgroup$ – Marcus Müller Dec 13 '17 at 21:50
1
$\begingroup$

The beauty of PCM (pulse-coded modulation, i.e. time samples of audio) is that the mathematical sum of two signals sounds like the acoustical superposition.

So, just add your two sines sample-wise.

$\endgroup$
  • 3
    $\begingroup$ huh, a downvote? That's democratically very fine with me, but I'd love to hear why! $\endgroup$ – Marcus Müller Dec 13 '17 at 12:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.