Advertisement

OpenAL multithreading problem

Started by December 19, 2004 07:04 PM
2 comments, last by _the_phantom_ 19 years, 9 months ago
I've designed a program that uses OpenAL to stream long sounds. But there seems a problem. I created the context with ALC API, then I queued four buffers for a source, then set up a multimedia timer using timeSetEvent with a callback function. In the callback function, I first check for thread reentrance (The multimedia timer runs in its own thread). if not, check if any buffer is processed. Then unqueue the buffer and fill it with new data and queue it again. The original thread no longer uses any OpenAL API calls. But after about 30 seconds, The program crashes in oal_wrap.dll. I was confused. I tried not using the timeSetEvent, but instead, service the buffer in the original thread using an INFINITE loop. No error occured. It seems that the buffer service program must run in the same thread that you created the context. I'll say again: There is no calls that access the same context simutaneously. Is it a bug?
any chance of seeing the code?
How about the error which appears?
which version of OpenAL are you using?
Rubbishing OpenAL because of a problem which might, or might not, be its fault is a little.. harsh.
Advertisement
OpenAL32.dll : 6.14.357.7
oal_wrap.dll : 1.0.1.0
Sound Card : Aureal AU8820 (Windows XP default driver)
OS : Windows XP SP1

The code is here (Some of them are useless at this time):
Need OGG VORBIS SDK

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <conio.h>
#include <math.h>
#include <al/al.h>
#include <al/alc.h>
#include <vorbis/vorbisfile.h>

#define AL_CLEAR_ERROR() alGetError()
#define AL_ERROR() (alGetError()!=AL_NO_ERROR)

ALubyte *string;

struct al_contxt
{
ALCdevice *dev;
ALCcontext *id;
};

struct al_buffer
{
ALuint id;
};

struct al_source
{
ALuint id;
};


struct al_stream_source
{
int numbuffers;
int blocksize;
int format;
int freq;
HANDLE thread;
ALuint *buffer_ids;
int numblocks;
int blocksplayed;
al_source source;
OggVorbis_File *vf;
char *data;
FILE *fp;
};

al_contxt *cur_contxt;

void al_check_error();

void al_buffer_destroy(al_buffer *buffer)
{
if(buffer->id)
{
alDeleteBuffers(1,&buffer->id);
buffer->id=0;
}
}

void al_source_destroy(al_source *source)
{
if(source->id)
{
alDeleteSources(1,&source->id);
source->id=0;
}
}

int al_source_create(al_source *source,al_buffer *buffer)
{
AL_CLEAR_ERROR();

alGenSources(1,&source->id);
if(AL_ERROR())
return 0;

if(buffer)
{
alSourcei(source->id,AL_BUFFER,buffer->id);

if(AL_ERROR())
{
al_source_destroy(source);
return 0;
}
}

return 1;
}

int al_buffer_create(al_buffer *buffer,char *file,void *data=0,int format=AL_FORMAT_MONO16,int size=24000,int freq=24000)
{
AL_CLEAR_ERROR();

alGenBuffers(1,&buffer->id);
if(AL_ERROR())
return 0;

// Feed data from memory
if(data)
{
alBufferData(buffer->id,format,data,size,freq);
}
else if(file)
{
FILE *fp=fopen(file,"rb");

if(!fp)
{
al_buffer_destroy(buffer);
return 0;
}

OggVorbis_File vf;

if(ov_open(fp,&vf,NULL,0)<0)
{
al_buffer_destroy(buffer);
fclose(fp);
return 0;
}

vorbis_info *info;

info=ov_info(&vf,-1);

if(info->channels==2)
format=AL_FORMAT_STEREO16;
else if(info->channels==1)
format=AL_FORMAT_MONO16;
else
{
al_buffer_destroy(buffer);
ov_clear(&vf);
fclose(fp);
return 0;
}

freq=info->rate;
size=ALsizei(2*info->channels*ov_pcm_total(&vf,-1));

void *data=malloc(size);
if(!data)
{
al_buffer_destroy(buffer);
ov_clear(&vf);
fclose(fp);
return 0;
}

int section;
int read;
char *pointer=(char*)data;

while(1)
{
read=ov_read(&vf,pointer,4096,0,2,1,&section);
// EOF
if(read==0)
break;
// Data
else if(read>0)
pointer+=read;
}

ov_clear(&vf);
fclose(fp);

alBufferData(buffer->id,format,data,size,freq);

free(data);
}

if(AL_ERROR())
{
al_buffer_destroy(buffer);
return 0;
}

return 1;
}

void al_stream_source_destroy(al_stream_source *source)
{
}

UINT al_stream_timer_id;
al_stream_source *streamsources[256];
int numstreamsources;

void CALLBACK al_stream_timer_callback(UINT id,UINT msg,DWORD user,DWORD dw1,DWORD dw2)
{
static LONG mutex;

if(!InterlockedExchange(&mutex,1))
{
int i;

// printf("Timer#");

for(i=0;i<256;i++)
{
if(streamsources)
{
int processed;
ALuint bufferid;

alGetSourcei(streamsources->source.id,AL_BUFFERS_PROCESSED,&processed);

while(processed)
{
alSourceUnqueueBuffers(streamsources->source.id,1,&bufferid);

int inbuffer;
alGetSourcei(streamsources->source.id,AL_BUFFERS_QUEUED,&inbuffer);

printf("Inq: %d\n",inbuffer);

char *pointer;
int bytesremain;
int read;
int section;

pointer=streamsources->data;
bytesremain=streamsources->blocksize;

while(bytesremain)
{
read=ov_read(streamsources->vf,pointer,bytesremain,0,2,1,&section);
if(read==0)
break;
else if(read>0)
{
pointer+=read;
bytesremain-=read;
}
}

alBufferData(bufferid,streamsources->format,streamsources->data,
streamsources->blocksize/*streamsources->blocksize-bytesremain*/,streamsources->freq);

alSourceQueueBuffers(streamsources->source.id,1,&bufferid);

processed--;
}
}
}

InterlockedExchange(&mutex,0);
}
}

int al_register_stream_timer(al_stream_source *source)
{
int i;

for(i=0;i<256;i++)
{
if(streamsources==source)
return 1;
}

for(i=0;i<256;i++)
{
if(!streamsources)
{
// Start the timer!
if(numstreamsources==0)
{
al_stream_timer_id=timeSetEvent(125,0,al_stream_timer_callback,
0,TIME_PERIODIC|TIME_CALLBACK_FUNCTION);
if(!al_stream_timer_id)
return 0;
}

streamsources=source;
numstreamsources++;

return 1;
}
}

return 0;
}

int al_stream_source_play(al_stream_source *source)
{
if(!al_register_stream_timer(source))
return 0;

AL_CLEAR_ERROR();
alSourcePlay(source->source.id);

al_register_stream_timer(source);

if(AL_ERROR())
{
//al_unregister_stream_timer(source);
return 0;
}

return 1;
}

int al_stream_source_create(al_stream_source *source,char *file,int numblocks=4,int blocktime=1)
{
int format,freq,blocksize;

AL_CLEAR_ERROR();

/* Open file */
source->fp=fopen(file,"rb");
if(!source->fp)
{
al_stream_source_destroy(source);
return 0;
}

/* Create an Ogg Vorbis file object */
source->vf=(OggVorbis_File*)malloc(sizeof(OggVorbis_File));
if(!source->vf)
{
al_stream_source_destroy(source);
return 0;
}

/* Open audio stream */
if(ov_open(source->fp,source->vf,NULL,0)<0)
{
al_stream_source_destroy(source);
return 0;
}

vorbis_info *info;

/* Get audio stream info */
info=ov_info(source->vf,-1);

if(info->channels==2)
format=AL_FORMAT_STEREO16;
else if(info->channels==1)
format=AL_FORMAT_MONO16;
else
{
al_stream_source_destroy(source);
return 0;
}

freq=info->rate;
blocksize=freq*2*info->channels*blocktime;

/* How many blocks in stream ? */
unsigned pcmtotal=unsigned(ov_pcm_total(source->vf,-1));
source->numblocks=pcmtotal/(info->rate*blocktime);
source->numblocks+=(pcmtotal%(info->rate*blocktime))?1:0;

/* How many buffers needed for stream? */
source->numbuffers=(source->numblocks>numblocks)?numblocks:source->numblocks;

/* Create buffer */
source->buffer_ids=(ALuint*)malloc(sizeof(ALuint)*source->numbuffers);
if(!source->buffer_ids)
{
al_stream_source_destroy(source);
return 0;
}

alGenBuffers(source->numbuffers,source->buffer_ids);
if(AL_ERROR())
{
al_stream_source_destroy(source);
return 0;
}

/* Temporatory data */
char *data=(char*)malloc(blocksize);
if(!data)
{
al_stream_source_destroy(source);
return 0;
}

int section;
int read,bytesremain;
char *pointer;

/* Fill the first blocks */
for(int i=0;i<numblocks;i++)
{
pointer=data;
bytesremain=blocksize;

while(bytesremain)
{
read=ov_read(source->vf,pointer,bytesremain,0,2,1,&section);
if(read==0)
break;
else if(read>0)
{
pointer+=read;
bytesremain-=read;
}
}

/* Write buffer */
alBufferData(source->buffer_ids,format,data,blocksize-bytesremain,freq);
if(AL_ERROR())
{
al_stream_source_destroy(source);
return 0;
}
}

source->data=data;
source->blocksplayed=0;
source->blocksize=blocksize;
source->format=format;
source->freq=freq;

/* Create source */
if(!al_source_create(&source->source,0))
{
al_stream_source_destroy(source);
return 0;
}

/* Loop must be set FALSE */
alSourcei(source->source.id,AL_LOOPING,0);

/* Queue Buffers */
alSourceQueueBuffers(source->source.id,source->numbuffers,source->buffer_ids);
if(AL_ERROR())
{
al_stream_source_destroy(source);
return 0;
}

return 1;
}

void al_destroy(al_contxt *contxt)
{
if(cur_contxt==contxt)
{
alcMakeContextCurrent(NULL);
cur_contxt=NULL;
}

if(contxt->id)
{
alcDestroyContext(contxt->id);
contxt->id=NULL;
}
if(contxt->dev)
{
alcCloseDevice(contxt->dev);
contxt->dev=NULL;
}
}

void al_check_error()
{
if(AL_ERROR())
{
al_destroy(cur_contxt);
printf("PANIC: OpenAL error!");
exit(0);
}
}

int al_select(al_contxt *contxt)
{
alcGetError(contxt->dev);
alcMakeContextCurrent(contxt->id);

if(alcGetError(contxt->dev)!=ALC_NO_ERROR)
return 0;

cur_contxt=contxt;

return 1;
}

int al_create(al_contxt *contxt,char *devstr)
{
contxt->dev=alcOpenDevice((ALubyte*)devstr);
if(!contxt->dev)
return 0;

contxt->id=alcCreateContext(contxt->dev,NULL);
if(!contxt->id)
{
al_destroy(contxt);
return 0;
}

return 1;
}

int main(int argc, char *argv[])
{
al_contxt ac;
al_buffer buf;
al_source src;
al_stream_source ssrc;
al_stream_source ssrc1;
ALfloat pos[3];///={0,0,0};
ALfloat vel[3];//={0,0,0};
ALfloat ori[6];//={0,0,-1,0,1,0};

if(!al_create(∾,NULL))
printf("fail!");
if(!al_select(∾))
printf("fail#!");

alListenerfv(AL_POSITION,pos);
alListenerfv(AL_VELOCITY,vel);
alListenerfv(AL_ORIENTATION,ori);
al_check_error();

// if(!al_buffer_create(&buf,"c:\\tz.ogg",NULL))
// {
// printf("Error opening file!");
// getch();
// return 0;
// }

// src.loop=AL_TRUE;
// src.pos[0]=src.pos[1]=src.pos[2]=0;
// src.dir[0]=src.dir[1]=0; src.dir[2]=1;
// src.vel[0]=src.vel[1]=src.vel[2]=0;

// al_source_create(&src,&buf);
// al_check_error();

// alSourcePlay(src.id);
// al_check_error();

al_stream_source_create(&ssrc,"c:\\onlyif.ogg",4,1);
//al_stream_source_create(&ssrc1,"c:\\onlyif.ogg");
al_stream_source_play(&ssrc);
al_stream_source_play(&ssrc1);

getch();
al_destroy(∾);

return 1;
}
i've saved off the code and i'll take a look at it tomorrow once i'm more awake and less full of vodka [smile]

This topic is closed to new replies.

Advertisement