Воспроизведение синусоидальной волны с изменяемыми параметрами — как сделать сдвиг фазы?

Я генерирую синусоидальную волну и посылаю ее в звуковой буфер SDL для генерации звука. Все параметры, такие как амплитуда и частота, могут быть изменены с помощью клавиш со стрелками на клавиатуре.

Теперь проблема в том, что когда я меняю частоту, я слышу «царапину». Я понимаю, почему это происходит: я получаю совершенно неверное значение, когда просто продолжаю повторять x в f(x) когда сама функция изменилась. Но я не вижу и не понимаю, как я могу решить эту проблему путем сдвига фаз.

Любые советы, как начать?

#include "WaveGenerator.h"#include <thread>
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>    // std::minint 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;
}
}

и заголовочный файл:

#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 secondclass 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

5

Решение

Самый простой способ изменить частоты без скачка по фазе, удалив audioPosition из уравнения:

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;
}
}

А потом, когда вы измените частоту, просто пересчитайте приращение фазы

m_sinePhaseInc = freq/sampleRate;
5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]