2012-04-11 22 views
6

Tengo problemas para entender cómo funciona la parte de audio de la biblioteca sdl ahora, sé que cuando lo inicializa, debe especificar la frecuencia y una >> devolución de llamada < < función , que creo que luego se llama automáticamente a la frecuencia dada. ¿Alguien que trabajó con la biblioteca sdl puede escribir un ejemplo simple que usaría sdl_audio para generar una onda cuadrada de 440 hz (dado que es la forma de onda más simple) a una frecuencia de muestreo de 44000 hz?Generador simple de ondas de sonido con SDL en C++

+0

Este es un muy buen ejemplo, la OMI: http://www.libsdl.org/intro.en/usingsound.html – jrok

+0

@jrok Mejorar su comentario y que sea una respuesta. Buena referencia. – karlphillip

+0

@karlphillip Allí, espero que esto cuente como una mejora;) – jrok

Respuesta

13

El Introduction to SDL tiene un claro ejemplo de la utilización de la biblioteca SDL sonido que debería empezar: http://www.libsdl.org/intro.en/usingsound.html

EDIT: Aquí hay un programa de trabajo que hace lo que pidió. He modificado un poco el código que se encuentra aquí: 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; 
} 

Buena suerte.

+0

gracias, pero todavía no puedo entenderlo. Soy un principiante absoluto con esta biblioteca. Antes de hacer la pregunta aquí, busqué para ver si alguien hizo la misma pregunta, pero todo lo que encontré fueron solo partes del código. Estoy seguro de que un programa que hace lo que le pedí sería muy corto ... –

+0

@Vlad Actualizó la respuesta. – jrok

+0

¡Gracias por eso! Mientras tanto, intenté escribir algo y obtuve esto: http: //docpaste.com/show.php? id = 231. La onda cuadrada es audible, pero tiene mucho ruido sobre ella. Y no es un ruido leve, casi cubre toda la salida de sonido. ¿Cuál puede ser el problema? –

2

2.0.2 ejemplo C

tomado de: 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; 
} 

probado en Ubuntu 15.10.

debe ser fácil de convertir esto en un simple piano con: https://github.com/cirosantilli/cpp-cheat/blob/f734a2e76fbcfc67f707ae06be7a2a2ef5db47d1/c/interactive/audio_gen.c#L44

Para la manipulación wav, compruebe también los ejemplos oficiales:

0

Un hervida - Variante a la baja del beeper-ejemplo, reducido al mínimo (con manejo de errores).

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