#include "sanitizedCarbon.h"
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include "quartzCommon.h"
#include "quartzAudio.h"
#include <CoreAudio/CoreAudio.h>
#include <pthread.h>
#include <AvailabilityMacros.h>
#include "inputstr.h"
#include <X11/extensions/XI.h>
#include <assert.h>
void NSBeep(void);
typedef struct QuartzAudioRec {
double frequency;
double amplitude;
UInt32 curFrame;
UInt32 remainingFrames;
UInt32 totalFrames;
UInt32 bytesPerFrame;
double sampleRate;
UInt32 fadeLength;
UInt32 bufferByteCount;
Boolean playing;
pthread_mutex_t lock;
double prevFrequency;
double prevAmplitude;
UInt32 prevFrame;
} QuartzAudioRec;
static AudioDeviceID quartzAudioDevice = kAudioDeviceUnknown;
static QuartzAudioRec data;
static double QuartzAudioEnvelope(
UInt32 curFrame,
UInt32 totalFrames,
UInt32 fadeLength )
{
double fadeFrames = min(fadeLength, totalFrames / 2);
if (fadeFrames < 1) return 0;
if (curFrame < fadeFrames) {
return curFrame / fadeFrames;
} else if (curFrame > totalFrames - fadeFrames) {
return (totalFrames-curFrame) / fadeFrames;
} else {
return 1.0;
}
}
static void QuartzFillBuffer(
AudioBuffer *audiobuffer,
QuartzAudioRec *data )
{
float *buffer, *b;
unsigned int frame, frameCount;
unsigned int bufferFrameCount;
float multiplier, v;
int i;
buffer = (float *)audiobuffer->mData;
bufferFrameCount = audiobuffer->mDataByteSize / data->bytesPerFrame;
frameCount = min(bufferFrameCount, data->remainingFrames);
b = buffer;
if (data->prevFrame) {
multiplier = 2*M_PI*(data->prevFrequency/data->sampleRate);
for (frame = 0; frame < data->fadeLength; frame++) {
v = data->prevAmplitude *
QuartzAudioEnvelope(frame+data->fadeLength,
2*data->fadeLength,
data->fadeLength) *
sin(multiplier * (data->prevFrame+frame));
for (i = 0; i < audiobuffer->mNumberChannels; i++) {
*b++ = v;
}
}
data->prevFrame = 0;
buffer += audiobuffer->mNumberChannels*frame;
bufferFrameCount -= frame;
frameCount = min(bufferFrameCount, data->remainingFrames);
}
multiplier = 2*M_PI*(data->frequency/data->sampleRate);
for (frame = 0; frame < frameCount; frame++) {
v = data->amplitude *
QuartzAudioEnvelope(data->curFrame+frame, data->totalFrames,
data->fadeLength) *
sin(multiplier * (data->curFrame+frame));
for (i = 0; i < audiobuffer->mNumberChannels; i++) {
*b++ = v;
}
}
memset(b, 0, sizeof(float) * audiobuffer->mNumberChannels *
(bufferFrameCount-frame));
data->curFrame += frameCount;
data->remainingFrames -= frameCount;
if (data->remainingFrames == 0) {
data->playing = FALSE;
data->curFrame = 0;
}
}
static OSStatus
QuartzAudioIOProc(
AudioDeviceID inDevice,
const AudioTimeStamp *inNow,
const AudioBufferList *inInputData,
const AudioTimeStamp *inInputTime,
AudioBufferList *outOutputData,
const AudioTimeStamp *inOutputTime,
void *inClientData )
{
QuartzAudioRec *data = (QuartzAudioRec *)inClientData;
int i;
Boolean wasPlaying;
pthread_mutex_lock(&data->lock);
wasPlaying = data->playing;
for (i = 0; i < outOutputData->mNumberBuffers; i++) {
if (data->playing) {
QuartzFillBuffer(outOutputData->mBuffers+i, data);
}
else {
memset(outOutputData->mBuffers[i].mData, 0,
outOutputData->mBuffers[i].mDataByteSize);
}
}
if (wasPlaying && !data->playing) {
OSStatus err;
err = AudioDeviceStop(inDevice, QuartzAudioIOProc);
}
pthread_mutex_unlock(&data->lock);
return 0;
}
void DDXRingBell(
int volume, int pitch, int duration ) {
if (quartzUseSysBeep) {
if (volume)
NSBeep();
return;
}
if (quartzAudioDevice == kAudioDeviceUnknown) return;
pthread_mutex_lock(&data.lock);
data.prevFrequency = data.frequency;
data.prevAmplitude = data.amplitude;
data.prevFrame = data.curFrame;
data.frequency = pitch;
data.amplitude = volume / 100.0;
data.curFrame = 0;
data.totalFrames = (int)(data.sampleRate * duration / 1000.0);
data.remainingFrames = data.totalFrames;
if (! data.playing) {
OSStatus status;
status = AudioDeviceStart(quartzAudioDevice, QuartzAudioIOProc);
if (status) {
ErrorF("DDXRingBell: AudioDeviceStart returned %ld\n", (long)status);
} else {
data.playing = TRUE;
}
}
pthread_mutex_unlock(&data.lock);
}
void QuartzAudioInit(void)
{
UInt32 propertySize;
OSStatus status;
AudioDeviceID outputDevice;
AudioStreamBasicDescription outputStreamDescription;
double sampleRate;
propertySize = sizeof(outputDevice);
status = AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultOutputDevice,
&propertySize, &outputDevice);
if (status) {
ErrorF("QuartzAudioInit: AudioHardwareGetProperty returned %ld\n",
(long)status);
return;
}
if (outputDevice == kAudioDeviceUnknown) {
ErrorF("QuartzAudioInit: No audio output devices available.\n");
return;
}
propertySize = sizeof(outputStreamDescription);
status = AudioDeviceGetProperty(outputDevice, 0, FALSE,
kAudioDevicePropertyStreamFormat,
&propertySize, &outputStreamDescription);
if (status) {
ErrorF("QuartzAudioInit: GetProperty(stream format) returned %ld\n",
(long)status);
return;
}
sampleRate = outputStreamDescription.mSampleRate;
data.frequency = 0;
data.amplitude = 0;
data.curFrame = 0;
data.remainingFrames = 0;
data.bytesPerFrame = outputStreamDescription.mBytesPerFrame;
data.sampleRate = sampleRate;
data.playing = FALSE;
data.prevAmplitude = 0;
data.prevFrame = 0;
data.prevFrequency = 0;
data.fadeLength = data.sampleRate / 200;
pthread_mutex_init(&data.lock, NULL);
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
{
AudioDeviceIOProcID sInputIOProcID = NULL;
status = AudioDeviceCreateIOProcID( outputDevice, QuartzAudioIOProc, &data, &sInputIOProcID );
}
#else
status = AudioDeviceAddIOProc(outputDevice, QuartzAudioIOProc, &data);
#endif
if (status) {
ErrorF("QuartzAudioInit: AddIOProc returned %ld\n", (long)status);
return;
}
quartzAudioDevice = outputDevice;
}