Working and tested audio backend for Solaris
Uses the audio device's "EOF counter" to tell how many audio buffers we've played, then ensures that production is three buffers ahead of playback. This has been tested and works. Either game or audio thread may still freeze, this can likely be fixed by implementing the locks.
This commit is contained in:
parent
a56c9c57a2
commit
6c04e37864
1 changed files with 52 additions and 44 deletions
|
@ -17,33 +17,46 @@
|
|||
#include <sys/audioio.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static const int samplesPerGo = 1600;
|
||||
static const int parkSignal = SIGUSR1;
|
||||
static pthread_t mainThread;
|
||||
static const int bufferSize = 300;
|
||||
static const int samplingRate = 8000;
|
||||
|
||||
static int buffersWritten = 0;
|
||||
static int sndfp;
|
||||
static FILE* sndfile;
|
||||
static void (*parent_callback)(long *stream, size_t frames_total);
|
||||
static bool audioDone = false;
|
||||
|
||||
static inline void setUpSoundFrame(long *mix) {
|
||||
memset(mix, 0, samplesPerGo * 2 * sizeof(long));
|
||||
parent_callback(mix, samplesPerGo);
|
||||
memset(mix, 0, bufferSize * 2 * sizeof(long));
|
||||
parent_callback(mix, bufferSize);
|
||||
}
|
||||
|
||||
static inline void soundWait() {
|
||||
static inline int soundWait() {
|
||||
// Wait until at least one audio buffer has been played back.
|
||||
// Return the number of audio buffers we have ever finished playing.
|
||||
|
||||
audio_info_t audioInfo;
|
||||
ioctl(sndfp, AUDIO_GETINFO, &audioInfo);
|
||||
int playedBefore = audioInfo.play.samples;
|
||||
int playedNow = audioInfo.play.samples;
|
||||
while(playedNow - playedBefore < samplesPerGo) {
|
||||
Backend_Delay(10);
|
||||
ioctl(sndfp, AUDIO_GETINFO, &audioInfo);
|
||||
playedNow = audioInfo.play.samples;
|
||||
|
||||
// audioInfo.play.eof is the number of "end-of-file records" the sound chip
|
||||
// has encountered. A "end-of-file record" is created by performing a
|
||||
// write() of zero length. We end each audio buffer with one such write.
|
||||
int previousEOF = audioInfo.play.eof;
|
||||
|
||||
while(audioInfo.play.eof - previousEOF < 1) {
|
||||
if(playedNow >= buffersWritten) {
|
||||
Backend_PrintError("Audio overflow.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
Backend_Delay(10); // Wait some reasonable time
|
||||
|
||||
ioctl(sndfp, AUDIO_GETINFO, &audioInfo); // Get the new current EOF
|
||||
}
|
||||
return playedNow;
|
||||
}
|
||||
|
||||
static inline void feedOutSound(long* mix) {
|
||||
for(int i = 0; i < samplesPerGo * 2; i++) {
|
||||
for(int i = 0; i < bufferSize * 2; i++) {
|
||||
short sample = mix[i];
|
||||
if(mix[i] > 0x7FFF) {
|
||||
sample = 0x7FFF;
|
||||
|
@ -54,69 +67,64 @@ static inline void feedOutSound(long* mix) {
|
|||
fputc((sample >> 8) & 0xFF, sndfile);
|
||||
fputc(sample & 0xFF, sndfile);
|
||||
}
|
||||
|
||||
// Write a "end-of-file record" here used as a end-of-buffer marker. The
|
||||
// sound chip counts how many of these it has encountered and we can use
|
||||
// that to tell how many sound buffers it's played.
|
||||
write(sndfp, 0, 0);
|
||||
fflush(sndfile);
|
||||
|
||||
buffersWritten++;
|
||||
}
|
||||
|
||||
static void* soundcheckthread(void*) {
|
||||
printf("Entered sound thread.\n");
|
||||
long mix[samplesPerGo * 2];
|
||||
setUpSoundFrame(mix);
|
||||
feedOutSound(mix);
|
||||
setUpSoundFrame(mix);
|
||||
feedOutSound(mix);
|
||||
static void* soundThread(void*) {
|
||||
long mix[bufferSize * 2];
|
||||
Backend_PrintInfo("Entered sound thread.\n");
|
||||
|
||||
while(1) {
|
||||
|
||||
setUpSoundFrame(mix);
|
||||
|
||||
// Waiting for more to be needed
|
||||
soundWait();
|
||||
int eofsPlayed = soundWait();
|
||||
|
||||
feedOutSound(mix);
|
||||
// Produce audio until we're three buffers ahead of playback
|
||||
while(buffersWritten - eofsPlayed < 3) {
|
||||
setUpSoundFrame(mix);
|
||||
feedOutSound(mix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void threadPark(int signo) {
|
||||
while(audioDone == false);
|
||||
}
|
||||
|
||||
unsigned long SoftwareMixerBackend_Init(void (*callback)(long *stream, size_t frames_total)) {
|
||||
parent_callback = callback;
|
||||
|
||||
if(sndfp == -1) {
|
||||
sndfile = fopen("/dev/audio", "wb");
|
||||
|
||||
if(sndfile == NULL) {
|
||||
Backend_PrintError("Failed to open audio device.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sndfile = fopen("/dev/audio", "wb");
|
||||
sndfp = fileno(sndfile);
|
||||
audio_info_t audioInfo;
|
||||
ioctl(sndfp, AUDIO_GETINFO, &audioInfo);
|
||||
audio_prinfo_t info = audioInfo.play;
|
||||
|
||||
info.sample_rate = 8000;
|
||||
info.sample_rate = samplingRate;
|
||||
info.channels = 2;
|
||||
info.precision = 8;
|
||||
info.encoding = AUDIO_ENCODING_LINEAR;
|
||||
info.port = AUDIO_SPEAKER | AUDIO_HEADPHONE;
|
||||
info.port = AUDIO_HEADPHONE;
|
||||
info.gain = 128;
|
||||
|
||||
audioInfo.play = info;
|
||||
ioctl(sndfp, AUDIO_SETINFO, &audioInfo);
|
||||
//close(sndfp);
|
||||
|
||||
if(signal(parkSignal, threadPark) == SIG_ERR) {
|
||||
Backend_PrintError("Failed to register thread park signal handler for audio system.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
mainThread = pthread_self();
|
||||
|
||||
pthread_t thread;
|
||||
pthread_attr_t thread_attr;
|
||||
pthread_attr_init(&thread_attr);
|
||||
|
||||
pthread_create(&thread, &thread_attr, soundcheckthread, (void*)NULL);
|
||||
pthread_create(&thread, &thread_attr, soundThread, (void*)NULL);
|
||||
|
||||
return 8000; // return the sampling rate
|
||||
return samplingRate; // Return the sampling rate
|
||||
}
|
||||
|
||||
void SoftwareMixerBackend_Deinit(void) {
|
||||
|
|
Loading…
Add table
Reference in a new issue