• Advertisement
Sign in to follow this  

sdl_audio: play five tones with exact duration

This topic is 3306 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys, I'm very new in programming with sdl and now I got a question. I'd like to play 5 tones of different frequencies (2000 - 2600 - 2400 - 1160 - 2600 Hz) each with an exact duration of 70ms. Currently my callback function that produces a tone looks like this: void play_tones(void* userdata, Uint8* stream, int len) { int i; int num_samples = len / 2; static int pos = 0; float buffer[BUFFER_SIZE]; Sint16 *dst_buf = (Sint16*) stream; for (i=0; i<num_samples ; ++i) buffer = sin(1160.0f*(i+pos)/(float)SAMPLING_RATE); // Clipping and conversion to Sint16 for (i=0; i<num_samples; ++i) { float v = buffer; if (v > 1.0f) v = 1.0f; else if (v< -1.0f) v = -1.0f; dst_buf = (Sint16)(32767.0f*v); } pos += num_samples; } BUFFER_SIZE is 1024 SAMPLING_RATE is 44100 I hope anybody can help me with that. Thanks a lot in advance. ratalert

Share this post


Link to post
Share on other sites
Advertisement
Well. If I would do that, then I would pre-record these tones, then load them and play them. But it is heavy to get those tones probably. So sorry I don't got a good idea.

Share this post


Link to post
Share on other sites
I can't record them before playing because the combination of tones in a serie differs every time.

for example once I'd like to play
2000 - 2600 - 2400 - 1160 - 2600 Hz
Next time
2200 - 1270 - 2400 - 1270 - 2600 Hz

The only thing which is not changing is every time five tones with a duration of 70ms.

Is there really no way?

Share this post


Link to post
Share on other sites
I'm not familiar with sdl_audio... but isn't there sdl_mixer?

I think you need to precompute your five buffers, and if sdl_mixer lives up to its name and has a sane interface, there should be no problem with it mixing five buffers at once in real time. I imagine you'd choose whatever 5 buffers it is this time around, and tell sdl_mixer to play them, and it'd mix them and play them all at once. And it appears I imagine correctly, if the docs are to be believed:

http://jcatki.no-ip.org:8080/SDL_mixer/SDL_mixer_frame.html

if your buffers are precomputed and called
buf1, buf2, buf3, buf4, buf5, and you randomly (or however) select 5 of them (with possible duplicates) into b1, b2, b3, b4, b5, then to play them, wouldn't you just do this?


Mix_PlayChannel(-1, b1, 1);
Mix_PlayChannel(-1, b2, 1);
Mix_PlayChannel(-1, b3, 1);
Mix_PlayChannel(-1, b4, 1);
Mix_PlayChannel(-1, b5, 1);





Share this post


Link to post
Share on other sites
alright, sounds good.

due to that I need to define 5 buffer with a tone in a particular frequency and play that first buffer 70ms, then I play the second buffer for the same time.

Is that possible with sdl_mixer?

Share this post


Link to post
Share on other sites
Quote:
Original post by ratalert
I hope anybody can help me with that. Thanks a lot in advance.

What is the actual problem you're having?

I would solve the problem this way:
- create a buffer big enough to hold your 5 tones (eg. 350ms, ie. an array pf 15435 Sint16s)
- generate each tone and fill this buffer with them, one after the other
- make your callback read a bit from that buffer each time

You don't need SDL_Mixer or any other kind of mixing for that.

Share this post


Link to post
Share on other sites
Kylotan, this is exactly what I want to do.
This is what I tried, it's not really correct I think - can you just please tell me what I'm doing wrong?

void play_tones(void* userdata, Uint8* stream, int len)
{
int size = 15435;
float buffer[15435];

Sint16 *dst_buf = (Sint16*) stream;
for (i=0; i<size/5 ; i++)
buffer = sin(2000.0f /(float)SAMPLING_RATE);
for (int x=i; x<size/5 ; x++)
buffer[x] = sin(2600 .0f /(float)SAMPLING_RATE);
for (int z=x; z<size/5 ; z++)
buffer[z] = sin(2400.0f /(float)SAMPLING_RATE);
for (int l=z; l<size/5 ; l++)
buffer[l] = sin(1160.0f /(float)SAMPLING_RATE);
for (int n=l; n<size/5 ; n++)
buffer[n] = sin(2600.0f /(float)SAMPLING_RATE);
}

int main()
{
SDL_Init(SDL_INIT_AUDIO);


audio.channels = 1; // Stereo = 2; Mono = 1
audio.format = AUDIO_S16; // Format
audio.freq = 44100; // 44 kHz
audio.samples = 1024; // 1024 Samples (PCM)
audio.callback = play_tones; // Callback function

if(SDL_OpenAudio(&audio, NULL) < 0)
return 1;

SDL_PauseAudio(0);
}

Share this post


Link to post
Share on other sites
You're not giving the sin() function a meaningful input so the value is constant. Something like this will work better (PI is, of course, 3.14159 and then some...):

buffer = sin( i * 2000.0f * 2 * PI /(float)SAMPLING_RATE);

Share this post


Link to post
Share on other sites
you're right. I changed it. The programm is running but I just get a continious tone not like the five tones I was expecting...

Share this post


Link to post
Share on other sites
Try showing the complete, current code? And put it in a [source][/source] box instead of code tags maybe?

Share this post


Link to post
Share on other sites
alright, thanks I did not know about these tags


#include "cplayer.h"
void play_tones(void*, Uint8*, int);

#define PI 3.14159

CPlayer::CPlayer()
{
Init();
}

CPlayer::~CPlayer()
{
}

void CPlayer::Init()
{
m_desired.freq = SAMPLING_RATE;
m_desired.format = AUDIO_S16SYS;
//audio.format = AUDIO_S16; // Format
m_desired.channels = 1;
m_desired.samples = BUFFER_SIZE;
m_desired.callback = play_tones;
m_desired.userdata = NULL;
}

bool CPlayer::Do()
{
if ( SDL_Init(SDL_INIT_AUDIO) < 0 ) {
//fprintf(stderr, "SDL konnte nicht initialisiert werden: %s\n", SDL_GetError());
return false;
}
if ( SDL_OpenAudio(&m_desired, NULL) < 0 ) {
//fprintf(stderr, "SDL konnte nicht geoeffnet werden: %s\n", SDL_GetError());
return false;
}
SDL_PauseAudio(0);
getchar();
SDL_PauseAudio(1);
SDL_CloseAudio();
SDL_Quit();

return true;
}

void play_tones(void* userdata, Uint8* stream, int len)
{
int i;
int num_samples = len / 2;
static int pos = 0;
int size = 15435;
float buffer[15435];

int x, z,l,n;

Sint16 *dst_buf = (Sint16*) stream;
for (i=0; i<size/5 ; i++)
buffer = sin( i * 2000.0f * 2 * PI /(float)SAMPLING_RATE);
for (x=i; x<size/5 ; x++)
buffer[x] = sin( i * 2600.0f * 2 * PI /(float)SAMPLING_RATE);
for (z=x; z<size/5 ; z++)
buffer[z] = sin( i * 2400.0f * 2 * PI /(float)SAMPLING_RATE);
for (l=z; l<size/5 ; l++)
buffer[l] = sin( i * 1600.0f * 2 * PI /(float)SAMPLING_RATE);
for (n=l; n<size/5 ; n++)
buffer[n] = sin( i * 2600.0f * 2 * PI /(float)SAMPLING_RATE);

// Clipping and conversion to Sint16
for (i=0; i<num_samples; ++i)
{
float v = buffer;
if (v > 1.0f)
v = 1.0f;
else if (v< -1.0f)
v = -1.0f;

dst_buf = (Sint16)(32767.0f*v);
}
pos += num_samples;
}

Share this post


Link to post
Share on other sites
You're using the variable "i" for all tones. I'm surprised that's even generating 350ms worth of sound, and not just 70ms, since "i" is constant after the first tone is generated.

Share this post


Link to post
Share on other sites
oh f***, damn copy & paste. thanks a lot ;)

nevertheless it produces just one raspy tone...

    int x, z,l,n;

Sint16 *dst_buf = (Sint16*) stream;
for (i=0; i<size/5 ; i++)
buffer = sin( i * 2000.0f * 2 * PI /(float)SAMPLING_RATE);
for (x=i; x<size/5 ; x++)
buffer[x] = sin( x * 2600.0f * 2 * PI /(float)SAMPLING_RATE);
for (z=x; z<size/5 ; z++)
buffer[z] = sin( z * 2400.0f * 2 * PI /(float)SAMPLING_RATE);
for (l=z; l<size/5 ; l++)
buffer[l] = sin( l * 1600.0f * 2 * PI /(float)SAMPLING_RATE);
for (n=l; n<size/5 ; n++)
buffer[n] = sin( n * 2600.0f * 2 * PI /(float)SAMPLING_RATE);


Share this post


Link to post
Share on other sites
I think I might see the problem.

You've specified an audio format of:

audio.format = AUDIO_S16; // Format

Which is to say, you've told it that the data will be in signed 16 bit little endian integers.

But, the buffer you're filling out is full of floats instead:

float buffer[BUFFER_SIZE];

Instead, you want:

int16_t buffer[BUFFER_SIZE];

It needs to be a buffer full of signed 16 bit little endian ints. (include inttypes.h or stdint.h and use int16_t, or, on most intel hardware a "signed short" will work too, but is slightly less portable.)

That would explain the raspy sound... floating point format data interpreted as signed shorts would look (and sound) like garbage, as I'm sure you noticed. :)

These lines of code:

buffer = sin(1160.0f*(i+pos)/(float)SAMPLING_RATE);


Should probably be something like:

buffer = (int16_t) (32767.0f * (sin(1160.0f*(i+pos)/(float)SAMPLING_RATE)));

(In my previous reply I had thought you were trying to get the 5 tones played simultaneously, mixed together, for a grand total of 70ms worth of sound -- now I realize that's not what you were asking. Sorry about that.)

Share this post


Link to post
Share on other sites
thanks a lot for the reply.

The tone I'm getting now doesn't sound raspy anymore but it's still just one continous tone.

void play_tones(void* userdata, Uint8* stream, int len)
{
int i;
int num_samples = len / 2;
static int pos = 0;
int size = 15435;
//float buffer[15435];
int16_t buffer[15435];

int x, z,l,n;

for (i=0; i<size/5 ; i++)
buffer = (int16_t) (32767.0f * (sin(2000.0f*(i+pos)/(float)SAMPLING_RATE)));
for (x=i; x<size/5 ; x++)
buffer[x] = (int16_t) (32767.0f * (sin(2600.0f*(i+pos)/(float)SAMPLING_RATE)));
for (z=x; z<size/5 ; z++)
buffer[z] = (int16_t) (32767.0f * (sin(2400.0f*(i+pos)/(float)SAMPLING_RATE)));
for (l=z; l<size/5 ; l++)
buffer[l] = (int16_t) (32767.0f * (sin(1600.0f*(i+pos)/(float)SAMPLING_RATE)));
for (n=l; n<size/5 ; n++)
buffer[n] = (int16_t) (32767.0f * (sin(2600.0f*(i+pos)/(float)SAMPLING_RATE)));



// Clipping and conversion to Sint16
for (i=0; i<num_samples; ++i)
{
float v = buffer;
if (v > 1.0f)
v = 1.0f;
else if (v< -1.0f)
v = -1.0f;

dst_buf = (Sint16)(32767.0f*v);
}
pos += num_samples;
}

Share this post


Link to post
Share on other sites
Uhhhhhh. Hmmmm.

I hadn't noticed that you had *another loop* that transformed the computed buffer full of floats into *another *(!!!!!) buffer full of signed 16 bit ints...

Uh, why compute the floats into a buffer only to transform the entire buffer to int16_t's?

And... given that you do that (which I hadn't previously noticed) why would computing the buffer of int16_t's directly, as I suggest, make a difference...

At this point, I give up.

Your code is buggy.

Advice: Debug it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement