blob: e8dca59b76568519198eab99d333a1ece8f412e4 [file] [log] [blame] [raw]
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include "sound.h"
//#define LOG_AUDIO
#define SND_BUF_MAX_READ_AHEAD 6
#define SND_LATENCY_IN_FRAGS 2
static int MixingFreq;
static unsigned int BufferLength;
static unsigned int sndBufferPos;
static unsigned int originalFrequency;
static short *sndRingBuffer;
static unsigned int sndWriteBufferIndex;
static unsigned int sndPlayBufferIndex;
static short *sndWriteBufferPtr;
static short *sndPlayBufferPtr;
static short lastSample;
static ClockCycle lastUpdateCycle;
static unsigned int lastSamplePos;
static SDL_AudioSpec *audiohwspec;
static void add_new_frag()
{
sndWriteBufferIndex++;
sndWriteBufferPtr = sndRingBuffer
+ (BufferLength * (sndWriteBufferIndex % SND_BUF_MAX_READ_AHEAD));
}
static void delete_frag()
{
sndPlayBufferIndex++;
sndPlayBufferPtr = sndRingBuffer
+ (BufferLength * (sndPlayBufferIndex % SND_BUF_MAX_READ_AHEAD));
}
static int getLeadInFrags()
{
return (int) (sndWriteBufferIndex - sndPlayBufferIndex);
}
static void fragmentDone()
{
int lead_in_frags = getLeadInFrags();
#ifdef LOG_AUDIO
fprintf(stderr, "Lead in frags: %i\n", lead_in_frags);
#endif
while (lead_in_frags < SND_LATENCY_IN_FRAGS) {
#ifdef LOG_AUDIO
fprintf(stderr, " adding an extra frag.\n");
#endif
render_audio(BufferLength, sndWriteBufferPtr);
add_new_frag();
lead_in_frags++;
}
}
static void audioCallback(void *userdata, Uint8 *stream, int len)
{
if (sndPlayBufferIndex < sndWriteBufferIndex) {
if (len > (int)(BufferLength*2))
len = BufferLength * 2;
memcpy(stream, sndPlayBufferPtr, len);
lastSample = sndPlayBufferPtr[len/2 - 1];
delete_frag();
} else {
short *buf = (short *) stream;
len /= 2;
do {
*buf++ = lastSample;
} while(--len);
}
#ifdef LOG_AUDIO
fprintf(stderr, "Playing a frag (%i).\n", getLeadInFrags());
#endif
}
void updateAudio(unsigned int nrsamples)
{
// SDL openaudio failed?
if (!sndWriteBufferPtr)
return;
if (sndBufferPos + nrsamples >= BufferLength) {
if (getLeadInFrags() > SND_LATENCY_IN_FRAGS) {
#ifdef LOG_AUDIO
fprintf(stderr, "Skipping a frag.\n");
#endif
} else {
// Finish pending buffer...
render_audio(BufferLength - sndBufferPos, sndWriteBufferPtr + sndBufferPos);
add_new_frag();
if ((sndBufferPos = (sndBufferPos + nrsamples) % BufferLength))
render_audio(sndBufferPos, sndWriteBufferPtr);
fragmentDone();
}
} else if (nrsamples) {
render_audio(nrsamples, sndWriteBufferPtr + sndBufferPos);
sndBufferPos += nrsamples;
}
//_ASSERT(sndBufferPos <= BufferLength);
}
static inline unsigned int getNrOfSamplesToGenerate(ClockCycle clock)
{
// OK this should really be INT but I'm tired right now
unsigned int samplePos = (unsigned int) ((double) clock * (double) MixingFreq / (originalFrequency * 8));
unsigned int samplesToDo = samplePos - lastSamplePos;
//fprintf( stderr, "Sound: %i cycles/%f samples\n", clock, (double) clock * (double) MixingFreq / (1778400.0/8.0));
lastSamplePos = samplePos;
return samplesToDo;
}
void flushBuffer(ClockCycle cycle)
{
updateAudio(getNrOfSamplesToGenerate(cycle));
lastUpdateCycle = cycle;
}
void init_audio(unsigned int soundFreq, unsigned int sampleFrq)
{
SDL_AudioSpec *desired, *obtained = NULL;
originalFrequency = soundFreq;
MixingFreq = sampleFrq;//44100 22050 11025 96000 48000
// Linux needs a buffer with a size of a factor of 2
BufferLength = 1024; // 512 1024 2048 4096
desired =(SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
obtained=(SDL_AudioSpec *) malloc(sizeof(SDL_AudioSpec));
desired->freq = MixingFreq;
desired->format = AUDIO_S16;
desired->channels = 1;
desired->samples = BufferLength;
desired->callback = audioCallback;
desired->userdata = NULL;
desired->size = desired->channels * desired->samples * sizeof(Uint8);
desired->silence = 0x00;
sndBufferPos = 0;
if (SDL_OpenAudio(desired, obtained)) {
fprintf(stderr,"SDL_OpenAudio failed!\n");
return;
} else {
fprintf(stderr,"SDL_OpenAudio success!\n");
fprintf(stderr, "Using audio driver : %s\n", SDL_GetCurrentAudioDriver());
if ( obtained == NULL ) {
fprintf(stderr, "Great! We have our desired audio format!\n");
audiohwspec = desired;
free(obtained);
} else {
//fprintf(stderr, "Oops! Failed to get desired audio format!\n");
audiohwspec = obtained;
free(desired);
}
}
MixingFreq = audiohwspec->freq;
BufferLength = audiohwspec->samples;
fprintf(stderr, "Obtained mixing frequency: %u\n",audiohwspec->freq);
fprintf(stderr, "Obtained audio format: %04X\n",audiohwspec->format);
fprintf(stderr, "Obtained channel number: %u\n",audiohwspec->channels);
fprintf(stderr, "Obtained audio buffer size: %u\n",audiohwspec->size);
fprintf(stderr, "Obtained sample buffer size: %u\n",audiohwspec->samples);
fprintf(stderr, "Obtained silence value: %u\n",audiohwspec->silence);
SDL_PauseAudio(0);
sndRingBuffer = new short[SND_BUF_MAX_READ_AHEAD * BufferLength];
for(unsigned int i = 0; i < SND_BUF_MAX_READ_AHEAD * BufferLength; i++)
sndRingBuffer[i] = audiohwspec->silence;
sndWriteBufferIndex = sndPlayBufferIndex = 0;
sndWriteBufferPtr = sndRingBuffer;
sndPlayBufferPtr = sndRingBuffer + BufferLength * SND_LATENCY_IN_FRAGS;
lastSample = 0;
lastUpdateCycle = 0;
lastSamplePos = 0;
//SDL_PauseAudio(1);
}
void sound_pause()
{
SDL_PauseAudio(1);
}
void sound_resume()
{
SDL_PauseAudio(0);
}
void close_audio()
{
SDL_PauseAudio(1);
SDL_Delay(15);
SDL_CloseAudio();
if ( audiohwspec )
free( audiohwspec );
delete [] sndRingBuffer;
}