#include "alutInternal.h"
#include <math.h>
#include <string.h>
#if defined(_WIN32)
#define random() rand()
#endif
static const double sampleFrequency = 44100;
/*
* The following waveformFoo functions expect the phase of the previous call and
* the current phase, both in the range [0..+1). They return an amplitude in the
* range [-1..+1].
*/
typedef double (*waveformFunction) (double lastPhase, double phase);
static double waveformSine(double UNUSED(lastPhase), double phase)
{
static const double pi = 3.14159265358979323846;
return sin(phase * pi);
}
static double waveformSquare(double UNUSED(lastPhase), double phase)
{
return (phase >= 0.5) ? -1 : 1;
}
static double waveformSawtooth(double UNUSED(lastPhase), double phase)
{
return 2 * phase - 1;
}
static double waveformWhitenoise(double UNUSED(lastPhase), double UNUSED(phase))
{
static const long prime = 67867967L;
return 2 * (double)(random() % prime) / prime - 1;
}
static double waveformImpulse(double lastPhase, double phase)
{
return (lastPhase > phase) ? 1 : 0;
}
static waveformFunction getWaveformFunction(ALenum waveshape)
{
switch (waveshape)
{
case ALUT_WAVEFORM_SINE:
return &waveformSine;
case ALUT_WAVEFORM_SQUARE:
return &waveformSquare;
case ALUT_WAVEFORM_SAWTOOTH:
return &waveformSawtooth;
case ALUT_WAVEFORM_WHITENOISE:
return &waveformWhitenoise;
case ALUT_WAVEFORM_IMPULSE:
return &waveformImpulse;
}
_alutSetError(ALUT_ERROR_INVALID_ENUM);
return NULL;
}
static OutputStream *generateWaveform(ALenum waveshape, ALfloat frequency, ALfloat phase, ALfloat duration)
{
waveformFunction func;
double sampleDuration, lastPhase, numSamplesD;
size_t numBytes, numSamples, i;
OutputStream *stream;
func = getWaveformFunction(waveshape);
if (func == NULL)
{
return NULL;
}
/* ToDo: Shall we test phase for [-180 .. +180]? */
if (frequency <= 0 || duration < 0)
{
_alutSetError(ALUT_ERROR_INVALID_VALUE);
return NULL;
}
/* allocate stream to hold AU header and sample data */
sampleDuration = floor((frequency * duration) + 0.5) / frequency;
/* GCC is a bit picky about casting function calls, so we do it in two
* steps... */
numSamplesD = floor(sampleDuration * sampleFrequency);
numSamples = (size_t) numSamplesD;
numBytes = numSamples * sizeof(int16_t);
stream = _alutOutputStreamConstruct(AU_HEADER_SIZE + numBytes);
if (stream == NULL)
{
return NULL;
}
/* write AU header for our 16bit mono data */
if (!_alutOutputStreamWriteInt32BE(stream, 0x2e736e64) || /* ".snd" */
!_alutOutputStreamWriteInt32BE(stream, AU_HEADER_SIZE) ||
!_alutOutputStreamWriteInt32BE(stream, (Int32BigEndian) numBytes) ||
!_alutOutputStreamWriteInt32BE(stream, AU_PCM_16) ||
!_alutOutputStreamWriteInt32BE(stream, (Int32BigEndian) sampleFrequency) || !_alutOutputStreamWriteInt32BE(stream, 1))
{
_alutOutputStreamDestroy(stream);
return NULL;
}
/* normalize phase from degrees */
phase /= 180;
/* the value corresponding to i = -1 below */
lastPhase = phase - frequency / sampleFrequency;
lastPhase -= floor(lastPhase);
/* calculate samples */
for (i = 0; i < numSamples; i++)
{
double p = phase + frequency * (double)i / sampleFrequency;
double currentPhase = p - floor(p);
double amplitude = func(lastPhase, currentPhase);
if (!_alutOutputStreamWriteInt16BE(stream, (Int16BigEndian) (amplitude * 32767)))
{
_alutOutputStreamDestroy(stream);
return NULL;
}
lastPhase = currentPhase;
}
return stream;
}
ALvoid *alutLoadMemoryWaveform(ALenum waveshape, ALfloat frequency, ALfloat phase, ALfloat duration, ALenum * format, ALsizei * size, ALfloat * freq)
{
OutputStream *outputStream;
InputStream *inputStream;
ALvoid *data;
if (!_alutSanityCheck())
{
return NULL;
}
outputStream = generateWaveform(waveshape, frequency, phase, duration);
if (outputStream == NULL)
{
return NULL;
}
/* We could do something more efficient here if the internal stream