Jessica Reznor wrote:
What sound driver you use? OSS , or somethin? let me know and may be I
can help you.
It appears that I’ve actually found why this happens.
It seems that FreeBSD’s native sound driver have a very long internal buffer (about
64KB or 3 seconds on 22KHz 8 bit sound), and all calls to select() after sending data
with write() erroneously returns immediately until this large buffer will be filled.
Thus, as the SDL uses separate thread to deal with sound, on application startup this
thread almost immediately fills this buffer (because select() do not block it from
doing this), so all next sounds in fact going first to the top of this buffer and
have to wait until previously filled data will be played.
To isolate this problem I’ve wrote a small testcase, which I believe could expose
this bug to the FreeBSD developers and now I’m looking for someone with Linux box who
will run this case on it and will send its output to me. This small program in
general behaves very similarly to the way the SDL’s audio works, however I’ve tried
to make as simple as possible.
Following is some results of my testcase running on FreeBSD box and what I think this
testcase should return in the case of correct driver implementation.
Sincerely,
Maxim
In theory output of this program should look like:
[n * Buffer written in 0 ms]
Buffer written in xxx ms
Buffer written in xxx ms
Buffer written in xxx ms
Buffer written in xxx ms
[… xxx ms]
where the number of “0 ms” strings will depend on number of fragments allocated in
the sound driver.
However, on the FreeBSD this program produced something like that:
Buffer written in 0 ms
Buffer written in 0 ms
[… 0 ms - repeated approx. 256 times]
Buffer written in 0 ms
Buffer written in xxx ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in xxx ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in xxx ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
Buffer written in 0 ms
…
So, it is clear that there is unwanted buffer with size of 256*256 bytes (64KB)
located somewhere down the wire.
-------------- next part --------------
#include <stdio.h>
#include <stdlib.h>
#include <machine/soundcard.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define BSIZE 256
unsigned char *buf;
static struct timeval *start = NULL;
void playbuf(int, unsigned char *);
void waitplay(int);
unsigned long getticks(void);
int main()
{
int audio_fd;
int i;
audio_buf_info info;
unsigned long curtime;
struct snd_size;
audio_fd = open("/dev/dsp", O_WRONLY | O_NONBLOCK);
i = 0x00020008;
ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);
ioctl(audio_fd, SNDCTL_DSP_SPEED, 11025);
ioctl(audio_fd, SNDCTL_DSP_SETFMT, AFMT_U8);
ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
printf("num=%d tot=%d siz=%d by=%d\n", info.fragments, info.fragstotal,
info.fragsize, info.bytes);
buf = malloc(BSIZE);
bzero(buf, BSIZE);
for(i=0;i<400;i++) {
curtime = getticks();
playbuf(audio_fd, buf);
waitplay(audio_fd);
printf("Buffer written in %lu ms\n", getticks() - curtime);
}
exit(0);
}
void playbuf(int fd, unsigned char *buf)
{
write(fd, buf, BSIZE);
}
void waitplay(int fd)
{
fd_set fdset;
struct timeval timeout;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
select(fd+1, NULL, &fdset, NULL, &timeout);
}
unsigned long getticks(void)
{
struct timeval now;
long ticks;
if (start == NULL) {
start = malloc(sizeof(struct timeval));
gettimeofday(start, NULL);
return(0);
}
gettimeofday(&now, NULL);
ticks=(now.tv_sec-start->tv_sec)*1000+(now.tv_usec-start->tv_usec)/1000;
return(ticks);
}