Simple sound wave generator with SDL in c++

前端 未结 4 537
孤城傲影
孤城傲影 2020-12-13 22:36

i am having problems understanding how the audio part of the sdl library works now, i know that when you initialize it, you have to specify the frequency and a >>callback<

相关标签:
4条回答
  • 2020-12-13 23:01

    SDL 2 C example

    The following code produces a sinusoidal sound, it is adapted from: https://codereview.stackexchange.com/questions/41086/play-some-sine-waves-with-sdl2

    main.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <SDL2/SDL.h>
    
    const double ChromaticRatio = 1.059463094359295264562;
    const double Tao = 6.283185307179586476925;
    
    Uint32 sampleRate = 48000;
    Uint32  frameRate =    60;
    Uint32 floatStreamLength = 1024;
    Uint32 samplesPerFrame;
    Uint32 msPerFrame;
    double practicallySilent = 0.001;
    
    Uint32 audioBufferLength = 48000;
    float *audioBuffer;
    
    SDL_atomic_t audioCallbackLeftOff;
    Sint32 audioMainLeftOff;
    Uint8 audioMainAccumulator;
    
    SDL_AudioDeviceID AudioDevice;
    SDL_AudioSpec audioSpec;
    
    SDL_Event event;
    SDL_bool running = SDL_TRUE;
    
    typedef struct {
        float *waveform;
        Uint32 waveformLength;
        double volume;
        double pan;
        double frequency;
        double phase;
    } voice;
    
    void speak(voice *v) {
        float sample;
        Uint32 sourceIndex;
        double phaseIncrement = v->frequency/sampleRate;
        Uint32 i;
        if (v->volume > practicallySilent) {
            for (i = 0; (i + 1) < samplesPerFrame; i += 2) {
                v->phase += phaseIncrement;
                if (v->phase > 1)
                    v->phase -= 1;
    
                sourceIndex = v->phase*v->waveformLength;
                sample = v->waveform[sourceIndex]*v->volume;
    
                audioBuffer[audioMainLeftOff+i] += sample*(1-v->pan);
                audioBuffer[audioMainLeftOff+i+1] += sample*v->pan;
            }
        }
        else {
            for (i=0; i<samplesPerFrame; i+=1)
                audioBuffer[audioMainLeftOff+i] = 0;
        }
        audioMainAccumulator++;
    }
    
    double getFrequency(double pitch) {
        return pow(ChromaticRatio, pitch-57)*440;
    }
    
    int getWaveformLength(double pitch) {
        return sampleRate / getFrequency(pitch)+0.5f;
    }
    
    void buildSineWave(float *data, Uint32 length) {
        Uint32 i;
        for (i=0; i < length; i++)
            data[i] = sin(i*(Tao/length));
    }
    
    void logSpec(SDL_AudioSpec *as) {
        printf(
            " freq______%5d\n"
            " format____%5d\n"
            " channels__%5d\n"
            " silence___%5d\n"
            " samples___%5d\n"
            " size______%5d\n\n",
            (int) as->freq,
            (int) as->format,
            (int) as->channels,
            (int) as->silence,
            (int) as->samples,
            (int) as->size
        );
    }
    
    void logVoice(voice *v) {
        printf(
            " waveformLength__%d\n"
            " volume__________%f\n"
            " pan_____________%f\n"
            " frequency_______%f\n"
            " phase___________%f\n",
            v->waveformLength,
            v->volume,
            v->pan,
            v->frequency,
            v->phase
        );
    }
    
    void logWavedata(float *floatStream, Uint32 floatStreamLength, Uint32 increment) {
        printf("\n\nwaveform data:\n\n");
        Uint32 i=0;
        for (i = 0; i < floatStreamLength; i += increment)
            printf("%4d:%2.16f\n", i, floatStream[i]);
        printf("\n\n");
    }
    
    void audioCallback(void *unused, Uint8 *byteStream, int byteStreamLength) {
        float* floatStream = (float*) byteStream;
        Sint32 localAudioCallbackLeftOff = SDL_AtomicGet(&audioCallbackLeftOff);
        Uint32 i;
        for (i = 0; i < floatStreamLength; i++) {
            floatStream[i] = audioBuffer[localAudioCallbackLeftOff];
            localAudioCallbackLeftOff++;
            if (localAudioCallbackLeftOff == audioBufferLength)
                localAudioCallbackLeftOff = 0;
        }
        SDL_AtomicSet(&audioCallbackLeftOff, localAudioCallbackLeftOff);
    }
    
    int init(void) {
        SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER);
        SDL_AudioSpec want;
        SDL_zero(want);
    
        want.freq = sampleRate;
        want.format = AUDIO_F32;
        want.channels = 2;
        want.samples = floatStreamLength;
        want.callback = audioCallback;
    
        AudioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &audioSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
        if (AudioDevice == 0) {
            printf("\nFailed to open audio: %s\n", SDL_GetError());
            return 1;
        }
    
        printf("want:\n");
        logSpec(&want);
        printf("audioSpec:\n");
        logSpec(&audioSpec);
    
        if (audioSpec.format != want.format) {
            printf("\nCouldn't get Float32 audio format.\n");
            return 2;
        }
    
        sampleRate = audioSpec.freq;
        floatStreamLength = audioSpec.size / 4;
        samplesPerFrame = sampleRate / frameRate;
        msPerFrame = 1000 / frameRate;
        audioMainLeftOff = samplesPerFrame * 8;
        SDL_AtomicSet(&audioCallbackLeftOff, 0);
    
        if (audioBufferLength % samplesPerFrame)
            audioBufferLength += samplesPerFrame - (audioBufferLength % samplesPerFrame);
        audioBuffer = malloc(sizeof(float) * audioBufferLength);
    
        return 0;
    }
    
    int onExit(void) {
        SDL_CloseAudioDevice(AudioDevice);
        SDL_Quit();
        return 0;
    }
    
    int main(int argc, char *argv[]) {
        float  syncCompensationFactor = 0.0016;
        Sint32 mainAudioLead;
        Uint32 i;
    
        voice testVoiceA;
        voice testVoiceB;
        voice testVoiceC;
        testVoiceA.volume = 1;
        testVoiceB.volume = 1;
        testVoiceC.volume = 1;
        testVoiceA.pan = 0.5;
        testVoiceB.pan = 0;
        testVoiceC.pan = 1;
        testVoiceA.phase = 0;
        testVoiceB.phase = 0;
        testVoiceC.phase = 0;
        testVoiceA.frequency = getFrequency(45);
        testVoiceB.frequency = getFrequency(49);
        testVoiceC.frequency = getFrequency(52);
        Uint16 C0waveformLength = getWaveformLength(0);
        testVoiceA.waveformLength = C0waveformLength;
        testVoiceB.waveformLength = C0waveformLength;
        testVoiceC.waveformLength = C0waveformLength;
        float sineWave[C0waveformLength];
        buildSineWave(sineWave, C0waveformLength);
        testVoiceA.waveform = sineWave;
        testVoiceB.waveform = sineWave;
        testVoiceC.waveform = sineWave;
    
        if (init())
            return 1;
    
        SDL_Delay(42);
        SDL_PauseAudioDevice(AudioDevice, 0);
        while (running) {
            while (SDL_PollEvent(&event) != 0) {
                if (event.type == SDL_QUIT) {
                    running = SDL_FALSE;
                }
            }
            for (i = 0; i < samplesPerFrame; i++)
                audioBuffer[audioMainLeftOff+i] = 0;
            speak(&testVoiceA);
            speak(&testVoiceB);
            speak(&testVoiceC);
            if (audioMainAccumulator > 1) {
                for (i=0; i<samplesPerFrame; i++) {
                    audioBuffer[audioMainLeftOff+i] /= audioMainAccumulator;
                }
            }
            audioMainAccumulator = 0;
            audioMainLeftOff += samplesPerFrame;
            if (audioMainLeftOff == audioBufferLength)
                audioMainLeftOff = 0;
            mainAudioLead = audioMainLeftOff - SDL_AtomicGet(&audioCallbackLeftOff);
            if (mainAudioLead < 0)
                mainAudioLead += audioBufferLength;
            if (mainAudioLead < floatStreamLength)
                printf("An audio collision may have occured!\n");
            SDL_Delay(mainAudioLead * syncCompensationFactor);
        }
        onExit();
        return 0;
    }
    

    Compile and run:

    gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -lSDL2 -lm
    ./main.out
    

    Should be easy to turn this into a simple piano with: https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44

    For wav manipulation, also check the official examples:

    • http://hg.libsdl.org/SDL/file/e12c38730512/test/testresample.c
    • http://hg.libsdl.org/SDL/file/e12c38730512/test/loopwave.c

    Tested on Ubuntu 19.10, SDL 2.0.10.

    0 讨论(0)
  • 2020-12-13 23:01

    This is a minimal example of how to play a sine wave in SDL2. Make sure to call SDL_Init(SDL_INIT_AUDIO) before creating an instance of Sound.

    Sound.h

    #include <cstdint>
    #include <SDL2/SDL.h>
    
    class Sound
    {
    public:
        Sound();
        ~Sound();
        void play();
        void stop();
    
        const double m_sineFreq;
        const double m_sampleFreq;
        const double m_samplesPerSine;
        uint32_t m_samplePos;
    
    private:
        static void SDLAudioCallback(void *data, Uint8 *buffer, int length);
    
        SDL_AudioDeviceID m_device;
    };
    
    

    Sound.cpp

    #include "Sound.h"
    #include <cmath>
    #include <iostream>
    
    Sound::Sound()
        : m_sineFreq(1000),
          m_sampleFreq(44100),
          m_samplesPerSine(m_sampleFreq / m_sineFreq),
          m_samplePos(0)
    {
        SDL_AudioSpec wantSpec, haveSpec;
    
        SDL_zero(wantSpec);
        wantSpec.freq = m_sampleFreq;
        wantSpec.format = AUDIO_U8;
        wantSpec.channels = 1;
        wantSpec.samples = 2048;
        wantSpec.callback = SDLAudioCallback;
        wantSpec.userdata = this;
    
        m_device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &haveSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
        if (m_device == 0)
        {
            std::cout << "Failed to open audio: " << SDL_GetError() << std::endl;
        }
    }
    
    Sound::~Sound()
    {
        SDL_CloseAudioDevice(m_device);
    }
    
    void Sound::play()
    {
        SDL_PauseAudioDevice(m_device, 0);
    }
    
    void Sound::stop()
    {
        SDL_PauseAudioDevice(m_device, 1);
    }
    
    void Sound::SDLAudioCallback(void *data, Uint8 *buffer, int length)
    {
        Sound *sound = reinterpret_cast<Sound*>(data);
    
        for(int i = 0; i < length; ++i)
        {
            buffer[i] = (std::sin(sound->m_samplePos / sound->m_samplesPerSine * M_PI * 2) + 1) * 127.5;
            ++sound->m_samplePos;
        }
    }
    
    
    0 讨论(0)
  • 2020-12-13 23:05

    The Introduction to SDL (2011 cached version: 2) has got a neat example of using SDL Sound library that should get you started: http://www.libsdl.org/intro.en/usingsound.html

    EDIT: Here is a working program that does what you asked for. I modified a bit the code found here: http://www.dgames.org/beep-sound-with-sdl/

    #include <SDL/SDL.h>
    #include <SDL/SDL_audio.h>
    #include <queue>
    #include <cmath>
    
    const int AMPLITUDE = 28000;
    const int FREQUENCY = 44100;
    
    struct BeepObject
    {
        double freq;
        int samplesLeft;
    };
    
    class Beeper
    {
    private:
        double v;
        std::queue<BeepObject> beeps;
    public:
        Beeper();
        ~Beeper();
        void beep(double freq, int duration);
        void generateSamples(Sint16 *stream, int length);
        void wait();
    };
    
    void audio_callback(void*, Uint8*, int);
    
    Beeper::Beeper()
    {
        SDL_AudioSpec desiredSpec;
    
        desiredSpec.freq = FREQUENCY;
        desiredSpec.format = AUDIO_S16SYS;
        desiredSpec.channels = 1;
        desiredSpec.samples = 2048;
        desiredSpec.callback = audio_callback;
        desiredSpec.userdata = this;
    
        SDL_AudioSpec obtainedSpec;
    
        // you might want to look for errors here
        SDL_OpenAudio(&desiredSpec, &obtainedSpec);
    
        // start play audio
        SDL_PauseAudio(0);
    }
    
    Beeper::~Beeper()
    {
        SDL_CloseAudio();
    }
    
    void Beeper::generateSamples(Sint16 *stream, int length)
    {
        int i = 0;
        while (i < length) {
    
            if (beeps.empty()) {
                while (i < length) {
                    stream[i] = 0;
                    i++;
                }
                return;
            }
            BeepObject& bo = beeps.front();
    
            int samplesToDo = std::min(i + bo.samplesLeft, length);
            bo.samplesLeft -= samplesToDo - i;
    
            while (i < samplesToDo) {
                stream[i] = AMPLITUDE * std::sin(v * 2 * M_PI / FREQUENCY);
                i++;
                v += bo.freq;
            }
    
            if (bo.samplesLeft == 0) {
                beeps.pop();
            }
        }
    }
    
    void Beeper::beep(double freq, int duration)
    {
        BeepObject bo;
        bo.freq = freq;
        bo.samplesLeft = duration * FREQUENCY / 1000;
    
        SDL_LockAudio();
        beeps.push(bo);
        SDL_UnlockAudio();
    }
    
    void Beeper::wait()
    {
        int size;
        do {
            SDL_Delay(20);
            SDL_LockAudio();
            size = beeps.size();
            SDL_UnlockAudio();
        } while (size > 0);
    
    }
    
    void audio_callback(void *_beeper, Uint8 *_stream, int _length)
    {
        Sint16 *stream = (Sint16*) _stream;
        int length = _length / 2;
        Beeper* beeper = (Beeper*) _beeper;
    
        beeper->generateSamples(stream, length);
    }
    
    int main(int argc, char* argv[])
    {
        SDL_Init(SDL_INIT_AUDIO);
    
        int duration = 1000;
        double Hz = 440;
    
        Beeper b;
        b.beep(Hz, duration);
        b.wait();
    
        return 0;
    }
    

    Good luck.

    0 讨论(0)
  • 2020-12-13 23:08

    A boiled-down variant of the beeper-example, reduced to the bare minimum (with error-handling).

    #include <math.h>
    #include <SDL.h>
    #include <SDL_audio.h>
    
    const int AMPLITUDE = 28000;
    const int SAMPLE_RATE = 44100;
    
    void audio_callback(void *user_data, Uint8 *raw_buffer, int bytes)
    {
        Sint16 *buffer = (Sint16*)raw_buffer;
        int length = bytes / 2; // 2 bytes per sample for AUDIO_S16SYS
        int &sample_nr(*(int*)user_data);
    
        for(int i = 0; i < length; i++, sample_nr++)
        {
            double time = (double)sample_nr / (double)SAMPLE_RATE;
            buffer[i] = (Sint16)(AMPLITUDE * sin(2.0f * M_PI * 441.0f * time)); // render 441 HZ sine wave
        }
    }
    
    int main(int argc, char *argv[])
    {
        if(SDL_Init(SDL_INIT_AUDIO) != 0) SDL_Log("Failed to initialize SDL: %s", SDL_GetError());
    
        int sample_nr = 0;
    
        SDL_AudioSpec want;
        want.freq = SAMPLE_RATE; // number of samples per second
        want.format = AUDIO_S16SYS; // sample type (here: signed short i.e. 16 bit)
        want.channels = 1; // only one channel
        want.samples = 2048; // buffer-size
        want.callback = audio_callback; // function SDL calls periodically to refill the buffer
        want.userdata = &sample_nr; // counter, keeping track of current sample number
    
        SDL_AudioSpec have;
        if(SDL_OpenAudio(&want, &have) != 0) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to open audio: %s", SDL_GetError());
        if(want.format != have.format) SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Failed to get the desired AudioSpec");
    
        SDL_PauseAudio(0); // start playing sound
        SDL_Delay(1000); // wait while sound is playing
        SDL_PauseAudio(1); // stop playing sound
    
        SDL_CloseAudio();
    
        return 0;
    }
    
    0 讨论(0)
提交回复
热议问题