Waveform generator using SDL audio

Hi,

I’m trying to implement a small waveform synthesis using the audio part of SDL. Finally, I will use it was a S-Funciton in MATLAB/Simulink, but for the time being, I’m trying to get it working in a simple Windows console program (Windows Vista 64bit, Visual Studio 2005 SP1).

The problem is that I can’t get a clear sound - there is always a kind of noise or ripple, no clear sound. In the following code I just want to play a sound at 800Hz for 5 seconds…

I gues it is somehow related to the specified sound format - I couldn’t get the point how to define the SDL_AudioSpec clearly - especially I was confused that unsigned variables are proposed to describe a sine wave.

Without the SDL_MixAudio mixer, I hear nothing. This may be a future problem, since finally, I have to superpose sine waves with different frequences at different volumes. As far as I got it, the SDL_MixAudio adjusts the volume…

Thanks a lot,

Tobias

#include “stdafx.h”
#include “SDL.h”
#include “SDL_audio.h”
#include <stdlib.h>
#include <math.h>

SDL_Surface *screen;
SDL_AudioSpec spec;
Uint32 sound_len;
Uint8 *sound_buffer;
int sound_pos = 0;
int counter;

//Do all the init stuff
void init_sdl (void)
{
if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
exit (-1);
atexit (SDL_Quit);
screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE);
if (screen == NULL)
exit (-1);
}

//Creates the sine wave. To supress ripple, the wave runs continuously by using an incemental counter
void Callback (void *userdata, Uint8 stream, int len)
{
double pi = 3.1415;
Uint8 waveptr;
double Hz=50;
double L = 512;
double A = 100;
double SR = 44100;
double F=2
pi
Hz/SR;

for (int z = 0; z< 512 ; z++) 
{
	counter++;
	sound_buffer[z] = (Uint8) A*sin(F*(double)counter);
}

//Do the sound loop...
if (sound_pos + len > sound_len)
{		
	sound_pos=0;
}

waveptr = sound_buffer + sound_pos;
SDL_MixAudio(stream, waveptr, len, SDL_MIX_MAXVOLUME);

//stream = waveptr; //Replacing the mixer gives nothing but silence...!?!
	
sound_pos += len;

}

void play (void)
{
sound_buffer = new Uint8[512];
sound_len= 512;
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 1;
spec.silence = 0;
spec.samples = 512;
spec.padding = 0;
spec.size = 0;
spec.userdata = 0;

spec.callback = Callback;
if (SDL_OpenAudio (&spec, NULL) < 0)
{
	printf ("Kann audio nicht ?ffnen: %s\n", SDL_GetError ());
	exit (-1);
}
SDL_PauseAudio (0);

}

int _tmain(int argc, _TCHAR* argv[])
{
init_sdl ();
play ();
SDL_Delay (5000);
return 0;

}

You need to sync your temporary buffer length (i.e. in your case z of
512 samples) to contain only a single cycle of the wave; i.e. check
sound_buffer[0] and sound_buffer[sound_len-1] … I am sure you’ll find
a discontinuity which will cause a noticeable click or scratch in your
output.

Also you want to initialize this “base sin wave” probably outside your
callback since it is called frequently to provide data to the output stream.

Maybe this helps:
http://code.google.com/p/freemat/wiki/TutorialMakeASinewave

–Andreas

Bruckmann, Tobias wrote:

Hi,

I’m trying to implement a small waveform synthesis using the audio part of SDL. Finally, I will use it was a S-Funciton in MATLAB/Simulink, but for the time being, I’m trying to get it working in a simple Windows console program (Windows Vista 64bit, Visual Studio 2005 SP1).

The problem is that I can’t get a clear sound - there is always a kind of noise or ripple, no clear sound. In the following code I just want to play a sound at 800Hz for 5 seconds…

I gues it is somehow related to the specified sound format - I couldn’t get the point how to define the SDL_AudioSpec clearly - especially I was confused that unsigned variables are proposed to describe a sine wave.

Without the SDL_MixAudio mixer, I hear nothing. This may be a future problem, since finally, I have to superpose sine waves with different frequences at different volumes. As far as I got it, the SDL_MixAudio adjusts the volume…

Thanks a lot,

Tobias

#include “stdafx.h”
#include “SDL.h”
#include “SDL_audio.h”
#include <stdlib.h>
#include <math.h>

SDL_Surface *screen;
SDL_AudioSpec spec;
Uint32 sound_len;
Uint8 *sound_buffer;
int sound_pos = 0;
int counter;

//Do all the init stuff
void init_sdl (void)
{
if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
exit (-1);
atexit (SDL_Quit);
screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE);
if (screen == NULL)
exit (-1);
}

//Creates the sine wave. To supress ripple, the wave runs continuously by using an incemental counter
void Callback (void *userdata, Uint8 stream, int len)
{
double pi = 3.1415;
Uint8 waveptr;
double Hz=50;
double L = 512;
double A = 100;
double SR = 44100;
double F=2
pi
Hz/SR;

for (int z = 0; z< 512 ; z++)
{
counter++;
sound_buffer[z] = (Uint8) Asin(F(double)counter);
}

//Do the sound loop…
if (sound_pos + len > s_len)
{
sound_pos=0;
}

waveptr = sound_buffer + sound_pos;
SDL_MixAudio(stream, waveptr, len, SDL_MIX_MAXVOLUME);

//stream = waveptr; //Replacing the mixer gives nothing but silence…!?!

sound_pos += len;
}

void play (void)
{
sound_buffer = new Uint8[512];
sound_len= 512;
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 1;
spec.silence = 0;
spec.samples = 512;
spec.padding = 0;
spec.size = 0;
spec.userdata = 0;

spec.callback = Callback;
if (SDL_OpenAudio (&spec, NULL) < 0)
{
printf (“Kann audio nicht ?ffnen: %s\n”, SDL_GetError ());
exit (-1);
}
SDL_PauseAudio (0);
}

int _tmain(int argc, _TCHAR* argv[])
{
init_sdl ();
play ();
SDL_Delay (5000);
return 0;

}


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

-------------- next part --------------
A non-text attachment was scrubbed…
Name: aschiffler.vcf
Type: text/x-vcard
Size: 135 bytes
Desc: not available
URL: http://lists.libsdl.org/pipermail/sdl-libsdl.org/attachments/20081022/810d0c79/attachment.vcf

Hi Andreas,

thanks a lot for your help.

The point you mention also came to my mind; this is why I introduced the variable “counter” which should “connect” the sine waves stemming from different calls of the callback function. Additionally, I tried to use very long buffers (e.g. 44100 samples at a sample rate of 44100 Hz -> 1 second) which should at least produce clear sound for one second, but even this fails.

The second point is: Finally, I have to change the frequency continuously - this is why I generate the wave directly in the callback function. I will allow me to react in a short time.

Is the used data data type (Uint, SInt16 etc.) correct? Which amplitudes sould I use for unsigned values?

Best regards,

Tobias

-----Urspr?ngliche Nachricht-----Von: sdl-bounces at lists.libsdl.org [mailto:sdl-bounces at lists.libsdl.org] Im Auftrag von Andreas Schiffler
Gesendet: Donnerstag, 23. Oktober 2008 04:54
An: A list for developers using the SDL library. (includes SDL-announce)
Betreff: Re: [SDL] Waveform generator using SDL audio

You need to sync your temporary buffer length (i.e. in your case z of
512 samples) to contain only a single cycle of the wave; i.e. check sound_buffer[0] and sound_buffer[sound_len-1] … I am sure you’ll find a discontinuity which will cause a noticeable click or scratch in your output.

Also you want to initialize this “base sin wave” probably outside your callback since it is called frequently to provide data to the output stream.

Maybe this helps:
http://code.google.com/p/freemat/wiki/TutorialMakeASinewave

–Andreas

Bruckmann, Tobias wrote:

Hi,

I’m trying to implement a small waveform synthesis using the audio part of SDL. Finally, I will use it was a S-Funciton in MATLAB/Simulink, but for the time being, I’m trying to get it working in a simple Windows console program (Windows Vista 64bit, Visual Studio 2005 SP1).

The problem is that I can’t get a clear sound - there is always a kind of noise or ripple, no clear sound. In the following code I just want to play a sound at 800Hz for 5 seconds…

I gues it is somehow related to the specified sound format - I couldn’t get the point how to define the SDL_AudioSpec clearly - especially I was confused that unsigned variables are proposed to describe a sine wave.

Without the SDL_MixAudio mixer, I hear nothing. This may be a future problem, since finally, I have to superpose sine waves with different frequences at different volumes. As far as I got it, the SDL_MixAudio adjusts the volume…

Thanks a lot,

Tobias

#include “stdafx.h”
#include “SDL.h”
#include “SDL_audio.h”
#include <stdlib.h>
#include <math.h>

SDL_Surface *screen;
SDL_AudioSpec spec;
Uint32 sound_len;
Uint8 *sound_buffer;
int sound_pos = 0;
int counter;

//Do all the init stuff
void init_sdl (void)
{
if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0)
exit (-1);
atexit (SDL_Quit);
screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE);
if (screen == NULL)
exit (-1);
}

//Creates the sine wave. To supress ripple, the wave runs continuously
by using an incemental counter void Callback (void *userdata, Uint8
stream, int len) {
double pi = 3.1415;
Uint8 waveptr;
double Hz=50;
double L = 512;
double A = 100;
double SR = 44100;
double F=2
pi
Hz/SR;

for (int z = 0; z< 512 ; z++)
{
counter++;
sound_buffer[z] = (Uint8) Asin(F(double)counter);
}

//Do the sound loop…
if (sound_pos + len > s_len)
{
sound_pos=0;
}

waveptr = sound_buffer + sound_pos;
SDL_MixAudio(stream, waveptr, len, SDL_MIX_MAXVOLUME);

//stream = waveptr; //Replacing the mixer gives nothing but silence…!?!

sound_pos += len;
}

void play (void)
{
sound_buffer = new Uint8[512];
sound_len= 512;
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 1;
spec.silence = 0;
spec.samples = 512;
spec.padding = 0;
spec.size = 0;
spec.userdata = 0;

spec.callback = Callback;
if (SDL_OpenAudio (&spec, NULL) < 0)
{
printf (“Kann audio nicht ?ffnen: %s\n”, SDL_GetError ());
exit (-1);
}
SDL_PauseAudio (0);
}

int _tmain(int argc, _TCHAR* argv[])
{
init_sdl ();
play ();
SDL_Delay (5000);
return 0;

}


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Hi Tobias

Is the used data data type (Uint, SInt16 etc.) correct? Which amplitudes sould I use for unsigned values?

That’s one of the problems. The unsigned data type will cut off your sine wave’s
negative part, or may be “wrap” it around. You should therefore use a bias to
"center" the sine wave within the data type’s range (0 to 255, if you stick with
Uint8; so the bias should be 127 or 128 and the amplitude should be no more than
127).

Also, I’m not sure if the cast to Uint8 in your formula works as desired. It
might cast A alone, causing it to lose precision before it is multiplied with
sin. But that wouldn’t cause sound distortion.

Try replacing
sound_buffer[z] = (Uint8) Asin(F(double)counter);
with
sound_buffer[z] = (Uint8) (Asin(F(double)counter)+127.0);

This should give a good sine wave in the buffer.
But the sound still won’t sound good, because:

SDL_MixAudio documentation says that the function will mix two buffers, adding
their samples (and some more). The destination buffer pointed to by stream might
not be reset to “silence” between callbacks. So you mix silence with sine wave,
new sine wave with old sine wave, and so on…

Talking about silence, there’s a member of that name in SDL_AudioSpec, which
should tell you the perfect bias (see above), at least if the samples are 8-bit

  • I’m a little confused why it’s just a Uint8 variable…

Next there’s the sample format. You set it to AUDIO_S16SYS, i. e. 16-bit signed,
but your samples actually are 8-bit unsigned. As you already guessed, that’s no
good. Either change the format to AUDIO_U8, or make Sint16 samples (silence
value is 0, so no bias needed, max. amplitude is 32767).
In the latter case, you must type cast the stream pointer (see example below).

About that comment:
//stream = waveptr; //Replacing the mixer gives nothing but silence…!?!

Assigning the pointer variable only copied the memory address of the buffer, not
its content. Since stream is a local variable of your Callback function, this
had no effect on the caller.
You would need to use memcpy(stream, waveptr, len) to copy the buffered data
from (*waveptr) to (*stream). But in this case, rather write the samples to the
output buffer directly.

Finally, you should generate exactly len BYTES (not samples) of audio data every
time, to stay in sync with audio output. Your code generates 512 samples on
every callback, no matter what len is. That sound_pos thing also looks very
suspicious.
Don’t forget that a Sint16 is two bytes, so the buffer can only hold len/2
Sint16 samples.

So I suggest:

//with format AUDIO_S16SYS
Sint16 * sound_buffer = (Sint16 )stream;
for (int z = 0; z<len/2; z++) {
counter++;
sound_buffer[z] = (Sint16)(A
sin(F*(double)counter));
}

Bye
Martin