I generate a sine wave and send it to the SDL sound buffer to generate sound. All parameters, such as amplitude and frequency, can be changed using the keyboard arrow keys.
Now the problem is that when I change the frequency, I hear a βscratchβ. I understand why this happens: I get a completely wrong value when I just keep repeating my x in f(x) , when the function itself has changed. But I do not see or do not understand how I can fix this problem by phase shift.
What clues 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 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