简单的声波发生器,带有SDL的c ++

时间:2012-04-11 17:26:05

标签: c++ audio sdl

我在理解sdl库的音频部分是如何工作时遇到了问题 现在,我知道当你初始化它时,你必须指定频率和>>回调<<函数,我认为然后在给定的频率自动调用。 任何使用过sdl库的人都可以编写一个简单的例子来使用sdl_audio来生成440赫兹的方波(因为它是最简单的波形),采样频率为44000赫兹吗?

4 个答案:

答案 0 :(得分:14)

Introduction to SDL有一个使用SDL Sound库的简洁示例,可以帮助您入门:http://www.libsdl.org/intro.en/usingsound.html

编辑:这是一个可以满足您要求的工作程序。我修改了一下这里找到的代码: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;
}
祝你好运。

答案 1 :(得分:2)

2.0.2 C示例

取自:https://codereview.stackexchange.com/questions/41086/play-some-sine-waves-with-sdl2

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

在Ubuntu 15.10上测试。

应该很容易将其转换为简单的钢琴:https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44

对于wav操作,还请查看官方示例:

答案 2 :(得分:2)

蜂鸣器示例的简化版本,减少到最低限度(使用错误处理)。

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

答案 3 :(得分:1)

这是如何在SDL2中播放正弦波的最小示例。 在创建SDL_Init(SDL_INIT_AUDIO)的实例之前,请确保先调用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;
    }
}