Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playing a Sine Wave with changable parameters - how to do the phase shifting?

I am generating a Sine wave and send it to the SDL Audio Buffer to generate sound. All the parameter like the amplitude and frequency can be changed with the arrow keys of the keyboard.

Now, the problem is that when I change the frequency, I hear a "scratch". I understand why this is happening: I'm getting a totally wrong value when I just continue iterating my x in f(x) when the function itself has changed. But I fail to see or understand how I can fix this problem by phase shifting.

Any tips how to start?

#include "WaveGenerator.h"
#include <thread>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>    // std::min


int main(int argc, char* argv[]){

    WaveGenerator* wg = new WaveGenerator();

    int i;
    std::cin >> i;
    return 0;
}

int graphThreadFunc(void *pointer){
    WaveGenerator* wg = (WaveGenerator*)pointer;
    wg->init();

    return 0;
}



// SDL calls this function whenever it wants its buffer to be filled with samples
// length = 2048
void SDLAudioCallback(void *data, Uint8 *buffer, int length){
    uint8_t *stream = (uint8_t*)buffer;

    WaveGenerator* wg = (WaveGenerator*)data;   // pointer to our WaveGenerator object where the voice data is stored

    for (int i = 0; i < length; i++){

        if (wg->voice.audioLength <= 0)
            stream[i] = wg->getSpec()->silence;      // 128 is silence in a uint8 stream
        else
        {
            stream[i] = wg->voice.getSample();      // calculate the current sample value

        }

        wg->voice.audioPosition++;
    }
}


WaveGenerator::WaveGenerator()
{
    // spawn thread
    SDL_Thread *refresh_thread = SDL_CreateThread(graphThreadFunc, NULL, this);
}

SDL_AudioSpec* WaveGenerator::getSpec(){
    return &this->spec;
}


void WaveGenerator::init()
{
    // Init SDL & SDL_ttf
    SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);

    SDL_zero(desiredDeviceSpec);

    desiredDeviceSpec.freq = SAMPLING_RATE;     // Sample Rate
    desiredDeviceSpec.format = AUDIO_U8;        // Unsigned 8-Bit Samples
    desiredDeviceSpec.channels = 1;             // Mono
    desiredDeviceSpec.samples = 2048;           // The size of the Audio Buffer (in number of samples, eg: 2048 * 1 Byte (AUDIO_U8)
    desiredDeviceSpec.callback = SDLAudioCallback;
    desiredDeviceSpec.userdata = this;


    dev = SDL_OpenAudioDevice(NULL, 0, &desiredDeviceSpec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
    if (dev == 0) {
        printf("\nFailed to open audio: %s\n", SDL_GetError());
    }
    else {
        SDL_PauseAudioDevice(dev, 1); /* pause! */
        SDL_PauseAudio(1);
    }

     //Create an application window with the following settings:
        window = SDL_CreateWindow(
            WINDOW_TITLE.c_str(),              // window title
            SDL_WINDOWPOS_UNDEFINED,           // initial x position
            SDL_WINDOWPOS_UNDEFINED,           // initial y position
            WINDOW_WIDTH,                      // width, in pixels
            WINDOW_HEIGHT,                     // height, in pixels
            SDL_WINDOW_SHOWN                  // flags - see below
            );

        // Check if the window was successfully created
        if (window == NULL) {
            // In case the window could not be created...
            printf("Could not create window: %s\n", SDL_GetError());
            return;
        }
        else{
            // Initial wave parameters
            voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
            voice.amp = 120;
            voice.frequency = 440;
            SDL_PauseAudioDevice(dev, 1);        // pause       
            voice.audioLength = SAMPLING_RATE;
            voice.audioPosition = 0;

            SDL_PauseAudioDevice(dev, 0);        // play
            SDL_Delay(SAMPLING_RATE / voice.audioLength * 1000);    // 44100 / length of the audio  * 1000 (to get milliseconds)

            mainLoop();
        }
    return;

}

void WaveGenerator::mainLoop()
{
    bool waveHasChanged = false;

    // poll SDL events until we terminate the thread
    while (thread_exit == 0){
        SDL_Event event;

        while (SDL_PollEvent(&event)) {
            switch (event.type)
            {
            case SDL_KEYDOWN:
            {       
                if (event.key.keysym.scancode == SDL_SCANCODE_SPACE){
                    switch (voice.waveForm){
                    case Voice::SINE:
                    {
                        voice.waveForm = WaveGenerator::Voice::WaveForm::TRIANGLE;
                        break;
                    }
                    case Voice::TRIANGLE:
                    {
                        voice.waveForm = WaveGenerator::Voice::WaveForm::RECT;
                        break;
                    }
                    case Voice::RECT:
                    {
                        voice.waveForm = WaveGenerator::Voice::WaveForm::SAWTOOTH;
                        break;
                    }
                    case Voice::SAWTOOTH:
                    {
                        voice.waveForm = WaveGenerator::Voice::WaveForm::NOISE;
                        break;
                    }
                    case Voice::NOISE:
                    {
                        voice.waveForm = WaveGenerator::Voice::WaveForm::SINE;
                        break;
                    }
                    default:
                        break;
                    }
                    waveHasChanged = true;
                }
                else if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE){
                    exit();
                }
                else if (event.key.keysym.scancode == SDL_SCANCODE_LEFT){               
                    voice.frequency -= 10;
                    waveHasChanged = true;
                }
                else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHT){                  
                    voice.frequency += 10;
                    waveHasChanged = true;
                }
                else if (event.key.keysym.scancode == SDL_SCANCODE_UP){                 
                    voice.amp += 2;
                    waveHasChanged = true;
                }
                else if (event.key.keysym.scancode == SDL_SCANCODE_DOWN){                   
                    voice.amp -= 2;
                    waveHasChanged = true;
                }
                else{

                }

                break;
            }

            case SDL_QUIT:
            {
                exit();
                return;
                break;
            }
            default: /* unhandled event */
                break;
            }
        }

        if (!pause_thread && waveHasChanged)
        {

            // calculate phase shifting?
        }


        SDL_Delay(50);
    }


    return;
}

void WaveGenerator::exit(){
    thread_exit = 1;
    // Clean up
    SDL_Quit();
}

WaveGenerator::Voice::Voice(){
}

uint8_t WaveGenerator::Voice::getSample(){

    switch (waveForm){
    case SINE:
    {
        return (amp * sin(2 * M_PI * audioPosition * frequency / SAMPLING_RATE)) + 128;
        break;
    }
    // .....
    default:
        return 0;
    }
}

and the header file:

#ifndef WAVEGENERATOR_H
#define WAVEGENERATOR_H
#include "SDL.h"
#include "SDL_audio.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include <stack>
#include <io.h> // unistd.h for mac/linux, io.h for windows
#include <vector>
#include <fstream>

/* Window Constants */
const std::string WINDOW_TITLE = "Wave Graph";
const int WINDOW_WIDTH = 1980;
const int WINDOW_HEIGHT = 255;

/* Audio Constants */
const int SAMPLING_RATE = 44100;                // number of samples per second


class WaveGenerator
{
private:
    SDL_Window *window;

    // SDL Audio
    SDL_AudioSpec desiredDeviceSpec;
    SDL_AudioSpec spec;
    SDL_AudioDeviceID dev;

    int thread_exit = 0;
    bool pause_thread = false;


public:
    WaveGenerator();
    void init();
    void mainLoop();

    void exit();
    SDL_AudioSpec* getSpec();


    // SDL audio members
    struct Voice{
        Voice();

        // WaveForm parameters
        enum WaveForm{
            SINE = 0, RECT = 1, SAWTOOTH = 2, TRIANGLE = 3, NOISE = 4
        } waveForm;
        int frequency;              // the frequency of the voice
        int amp;                    // the amplitude of the voice


        // SDL buffer handling members
        int audioLength;            // number of samples to be played, eg: 1.2 seconds * 44100 samples per second
        int audioPosition = 0;      // counter

        uint8_t getSample();


    } voice;

};

#endif
like image 511
user66875 Avatar asked Nov 08 '15 14:11

user66875


People also ask

How do you find the phase difference between two sine waves?

The phase shift equation is ps = 360 * td / p, where ps is the phase shift in degrees, td is the time difference between waves and p is the wave period.

Does sine wave have a phase?

Sine waves that are perfectly aligned peak to peak are called in phase. If one wave is shifted by half a wavelength (relative to the other), the troughs of one wave are aligned with peaks of the other and the waves are called perfectly (or completely) out of phase.

What is the effect of the phase angle on the graphical representation of a sine wave?

The phase angle of a sine wave can be used to describe the relationship of one sine wave to another by using the terms “Leading” and “Lagging” to indicate the relationship between two sinusoidal waveforms of the same frequency, plotted onto the same reference axis.

How do you control a sine wave?

To change the frequency of a sine wave generated by the “osc~” object you need to send the frequency in Hz to the hot inlet. To control the phase of a sine wave you can set it on the right inlet of osc~. This will set the phase of the repeating waveform; any new input will reset the phase.


1 Answers

The easiest way to change frequencies without a jump in phase by removing audioPosition from the equation:

class WaveGenerator
{
private:
    double m_sinePhase;
    double m_sinePhaseInc;

uint8_t WaveGenerator::Voice::getSample(){
    switch (waveForm){
    case SINE:
    {
        uint8_t sample = (amp * sin(2 * M_PI * m_sinePhase)) + 128;
        m_sinePhase += m_sinePhaseInc;
        return sample;
    }
}

And then when you change the frequency just recompute the phase increment

m_sinePhaseInc = freq/sampleRate;
like image 136
jaket Avatar answered Nov 14 '22 22:11

jaket