Blame synfig-osx/launcher/quartz-audio.c

Carlos Lopez a09598
//
Carlos Lopez a09598
// QuartzAudio.m
Carlos Lopez a09598
//
Carlos Lopez a09598
// X Window bell support using CoreAudio or AppKit.
Carlos Lopez a09598
// Greg Parker  gparker@cs.stanford.edu  19 Feb 2001
Carlos Lopez a09598
//
Carlos Lopez a09598
// Info about sine wave sound playback:
Carlos Lopez a09598
// CoreAudio code derived from macosx-dev posting by Tim Wood
Carlos Lopez a09598
//  http://www.omnigroup.com/mailman/archive/macosx-dev/2000-May/002004.html
Carlos Lopez a09598
// Smoothing transitions between sounds
Carlos Lopez a09598
//  http://www.wam.umd.edu/~mphoenix/dss/dss.html
Carlos Lopez a09598
//
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
Carlos Lopez a09598
 *
Carlos Lopez a09598
 * Permission is hereby granted, free of charge, to any person obtaining a
Carlos Lopez a09598
 * copy of this software and associated documentation files (the "Software"),
Carlos Lopez a09598
 * to deal in the Software without restriction, including without limitation
Carlos Lopez a09598
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
Carlos Lopez a09598
 * and/or sell copies of the Software, and to permit persons to whom the
Carlos Lopez a09598
 * Software is furnished to do so, subject to the following conditions:
Carlos Lopez a09598
 *
Carlos Lopez a09598
 * The above copyright notice and this permission notice shall be included in
Carlos Lopez a09598
 * all copies or substantial portions of the Software.
Carlos Lopez a09598
 *
Carlos Lopez a09598
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Carlos Lopez a09598
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Carlos Lopez a09598
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Carlos Lopez a09598
 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
Carlos Lopez a09598
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
Carlos Lopez a09598
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Carlos Lopez a09598
 * DEALINGS IN THE SOFTWARE.
Carlos Lopez a09598
 *
Carlos Lopez a09598
 * Except as contained in this notice, the name(s) of the above copyright
Carlos Lopez a09598
 * holders shall not be used in advertising or otherwise to promote the sale,
Carlos Lopez a09598
 * use or other dealings in this Software without prior written authorization.
Carlos Lopez a09598
 */
Carlos Lopez a09598
/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/quartzAudio.c,v 1.1 2002/03/28 02:21:18 torrey Exp $ */
Carlos Lopez a09598
Carlos Lopez a09598
#include "quartz.h"
Carlos Lopez a09598
#include "quartz-audio.h"
Carlos Lopez a09598
Carlos Lopez a09598
#include <coreaudio coreaudio.h=""></coreaudio>
Carlos Lopez a09598
#include <pthread.h></pthread.h>
Carlos Lopez a09598
Carlos Lopez a09598
#include "inputstr.h"
Carlos Lopez a09598
#include "extensions/XI.h"
Carlos Lopez a09598
Carlos Lopez a09598
void NSBeep();
Carlos Lopez a09598
Carlos Lopez a09598
typedef struct QuartzAudioRec {
Carlos Lopez a09598
    double frequency;
Carlos Lopez a09598
    double amplitude;
Carlos Lopez a09598
Carlos Lopez a09598
    UInt32 curFrame;
Carlos Lopez a09598
    UInt32 remainingFrames;
Carlos Lopez a09598
    UInt32 totalFrames;
Carlos Lopez a09598
    UInt32 bytesPerFrame;
Carlos Lopez a09598
    double sampleRate;
Carlos Lopez a09598
    UInt32 fadeLength;
Carlos Lopez a09598
Carlos Lopez a09598
    UInt32 bufferByteCount;
Carlos Lopez a09598
    Boolean playing;
Carlos Lopez a09598
    pthread_mutex_t lock;
Carlos Lopez a09598
Carlos Lopez a09598
    // used to fade out interrupted sound and avoid 'pop'
Carlos Lopez a09598
    double prevFrequency;
Carlos Lopez a09598
    double prevAmplitude;
Carlos Lopez a09598
    UInt32 prevFrame; 
Carlos Lopez a09598
} QuartzAudioRec;
Carlos Lopez a09598
Carlos Lopez a09598
static AudioDeviceID quartzAudioDevice = kAudioDeviceUnknown;
Carlos Lopez a09598
static QuartzAudioRec data;
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzAudioEnvelope
Carlos Lopez a09598
 *  Fade sound in and out to avoid pop.
Carlos Lopez a09598
 *  Sounds with shorter duration will never reach full amplitude. Deal.
Carlos Lopez a09598
 */
Carlos Lopez a09598
static double QuartzAudioEnvelope(
Carlos Lopez a09598
    UInt32 curFrame,
Carlos Lopez a09598
    UInt32 totalFrames,
Carlos Lopez a09598
    UInt32 fadeLength )
Carlos Lopez a09598
{
Carlos Lopez a09598
    double fadeFrames = min(fadeLength, totalFrames / 2);
Carlos Lopez a09598
    if (fadeFrames < 1) return 0;
Carlos Lopez a09598
Carlos Lopez a09598
    if (curFrame < fadeFrames) {
Carlos Lopez a09598
        return curFrame / fadeFrames;
Carlos Lopez a09598
    } else if (curFrame > totalFrames - fadeFrames) {
Carlos Lopez a09598
        return (totalFrames-curFrame) / fadeFrames;
Carlos Lopez a09598
    } else {
Carlos Lopez a09598
        return 1.0;
Carlos Lopez a09598
    }
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzFillBuffer
Carlos Lopez a09598
 *  Fill this buffer with data and update the data position.
Carlos Lopez a09598
 *  FIXME: this is ugly
Carlos Lopez a09598
 */
Carlos Lopez a09598
static void QuartzFillBuffer(
Carlos Lopez a09598
    AudioBuffer *audiobuffer,
Carlos Lopez a09598
    QuartzAudioRec *data )
Carlos Lopez a09598
{
Carlos Lopez a09598
    float *buffer, *b;
Carlos Lopez a09598
    unsigned int frame, frameCount;
Carlos Lopez a09598
    unsigned int bufferFrameCount;
Carlos Lopez a09598
    float multiplier, v;
Carlos Lopez a09598
    int i;
Carlos Lopez a09598
Carlos Lopez a09598
    buffer = (float *)audiobuffer->mData;
Carlos Lopez a09598
    bufferFrameCount = audiobuffer->mDataByteSize / data->bytesPerFrame;
Carlos Lopez a09598
Carlos Lopez a09598
    frameCount = min(bufferFrameCount, data->remainingFrames);
Carlos Lopez a09598
Carlos Lopez a09598
    // Fade out previous sine wave, if any.
Carlos Lopez a09598
    b = buffer;
Carlos Lopez a09598
    if (data->prevFrame) {
Carlos Lopez a09598
        multiplier = 2*M_PI*(data->prevFrequency/data->sampleRate);
Carlos Lopez a09598
        for (frame = 0; frame < data->fadeLength; frame++) {
Carlos Lopez a09598
            v = data->prevAmplitude *
Carlos Lopez a09598
                QuartzAudioEnvelope(frame+data->fadeLength,
Carlos Lopez a09598
                                    2*data->fadeLength,
Carlos Lopez a09598
                                    data->fadeLength) *
Carlos Lopez a09598
                sin(multiplier * (data->prevFrame+frame));
Carlos Lopez a09598
            for (i = 0; i < audiobuffer->mNumberChannels; i++) {
Carlos Lopez a09598
                *b++ = v;
Carlos Lopez a09598
            }
Carlos Lopez a09598
        }
Carlos Lopez a09598
        // no more prev fade
Carlos Lopez a09598
        data->prevFrame = 0;
Carlos Lopez a09598
Carlos Lopez a09598
        // adjust for space eaten by prev fade
Carlos Lopez a09598
        buffer += audiobuffer->mNumberChannels*frame;
Carlos Lopez a09598
        bufferFrameCount -= frame;
Carlos Lopez a09598
        frameCount = min(bufferFrameCount, data->remainingFrames);
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
    // Write a sine wave with the specified frequency and amplitude
Carlos Lopez a09598
    multiplier = 2*M_PI*(data->frequency/data->sampleRate);
Carlos Lopez a09598
    for (frame = 0; frame < frameCount; frame++) {
Carlos Lopez a09598
        v = data->amplitude * 
Carlos Lopez a09598
            QuartzAudioEnvelope(data->curFrame+frame, data->totalFrames,
Carlos Lopez a09598
                                data->fadeLength) *
Carlos Lopez a09598
            sin(multiplier * (data->curFrame+frame));
Carlos Lopez a09598
        for (i = 0; i < audiobuffer->mNumberChannels; i++) {
Carlos Lopez a09598
            *b++ = v;
Carlos Lopez a09598
        }
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
    // Zero out the rest of the buffer, if any
Carlos Lopez a09598
    memset(b, 0, sizeof(float) * audiobuffer->mNumberChannels *
Carlos Lopez a09598
           (bufferFrameCount-frame));
Carlos Lopez a09598
Carlos Lopez a09598
    data->curFrame += frameCount;
Carlos Lopez a09598
    data->remainingFrames -= frameCount;
Carlos Lopez a09598
    if (data->remainingFrames == 0) {
Carlos Lopez a09598
        data->playing = FALSE;
Carlos Lopez a09598
        data->curFrame = 0;
Carlos Lopez a09598
    }
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzAudioIOProc
Carlos Lopez a09598
 *  Callback function for audio playback.
Carlos Lopez a09598
 *  FIXME: use inOutputTime to correct for skipping
Carlos Lopez a09598
 */
Carlos Lopez a09598
static OSStatus 
Carlos Lopez a09598
QuartzAudioIOProc(
Carlos Lopez a09598
    AudioDeviceID inDevice, 
Carlos Lopez a09598
    const AudioTimeStamp *inNow, 
Carlos Lopez a09598
    const AudioBufferList *inInputData, 
Carlos Lopez a09598
    const AudioTimeStamp *inInputTime, 
Carlos Lopez a09598
    AudioBufferList *outOutputData, 
Carlos Lopez a09598
    const AudioTimeStamp *inOutputTime, 
Carlos Lopez a09598
    void *inClientData )
Carlos Lopez a09598
{
Carlos Lopez a09598
    QuartzAudioRec *data = (QuartzAudioRec *)inClientData;
Carlos Lopez a09598
    int i;
Carlos Lopez a09598
    Boolean wasPlaying;
Carlos Lopez a09598
Carlos Lopez a09598
    pthread_mutex_lock(&data->lock);
Carlos Lopez a09598
    wasPlaying = data->playing;
Carlos Lopez a09598
    for (i = 0; i < outOutputData->mNumberBuffers; i++) {
Carlos Lopez a09598
        if (data->playing) {
Carlos Lopez a09598
            QuartzFillBuffer(outOutputData->mBuffers+i, data); 
Carlos Lopez a09598
        }
Carlos Lopez a09598
        else {
Carlos Lopez a09598
            memset(outOutputData->mBuffers[i].mData, 0, 
Carlos Lopez a09598
                   outOutputData->mBuffers[i].mDataByteSize);
Carlos Lopez a09598
        }
Carlos Lopez a09598
    }
Carlos Lopez a09598
    if (wasPlaying  &&  !data->playing) {
Carlos Lopez a09598
        OSStatus err;
Carlos Lopez a09598
        err = AudioDeviceStop(inDevice, QuartzAudioIOProc);
Carlos Lopez a09598
    }
Carlos Lopez a09598
    pthread_mutex_unlock(&data->lock);
Carlos Lopez a09598
    return 0;
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzCoreAudioBell
Carlos Lopez a09598
 *  Play a tone using the CoreAudio API
Carlos Lopez a09598
 */
Carlos Lopez a09598
static void QuartzCoreAudioBell(
Carlos Lopez a09598
    int volume,         // volume is % of max
Carlos Lopez a09598
    int pitch,          // pitch is Hz
Carlos Lopez a09598
    int duration )      // duration is milliseconds
Carlos Lopez a09598
{
Carlos Lopez a09598
    if (quartzAudioDevice == kAudioDeviceUnknown) return;
Carlos Lopez a09598
Carlos Lopez a09598
    pthread_mutex_lock(&data.lock);
Carlos Lopez a09598
Carlos Lopez a09598
    // fade previous sound, if any
Carlos Lopez a09598
    data.prevFrequency = data.frequency;
Carlos Lopez a09598
    data.prevAmplitude = data.amplitude;
Carlos Lopez a09598
    data.prevFrame = data.curFrame;
Carlos Lopez a09598
Carlos Lopez a09598
    // set new sound
Carlos Lopez a09598
    data.frequency = pitch;
Carlos Lopez a09598
    data.amplitude = volume / 100.0;
Carlos Lopez a09598
    data.curFrame = 0;
Carlos Lopez a09598
    data.totalFrames = (int)(data.sampleRate * duration / 1000.0);
Carlos Lopez a09598
    data.remainingFrames = data.totalFrames;
Carlos Lopez a09598
Carlos Lopez a09598
    if (! data.playing) {
Carlos Lopez a09598
        OSStatus status;
Carlos Lopez a09598
        status = AudioDeviceStart(quartzAudioDevice, QuartzAudioIOProc);
Carlos Lopez a09598
        if (status) {
Carlos Lopez a09598
            ErrorF("QuartzAudioBell: AudioDeviceStart returned %d\n", status);
Carlos Lopez a09598
        } else {
Carlos Lopez a09598
            data.playing = TRUE;
Carlos Lopez a09598
        }
Carlos Lopez a09598
    }
Carlos Lopez a09598
    pthread_mutex_unlock(&data.lock);
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzBell
Carlos Lopez a09598
 *  Ring the bell
Carlos Lopez a09598
 */
Carlos Lopez a09598
void QuartzBell(
Carlos Lopez a09598
    int volume,             // volume in percent of max
Carlos Lopez a09598
    DeviceIntPtr pDevice,
Carlos Lopez a09598
    pointer ctrl,
Carlos Lopez a09598
    int class )
Carlos Lopez a09598
{
Carlos Lopez a09598
    int pitch;              // pitch in Hz
Carlos Lopez a09598
    int duration;           // duration in milliseconds
Carlos Lopez a09598
Carlos Lopez a09598
    if (class == BellFeedbackClass) {
Carlos Lopez a09598
        pitch = ((BellCtrl*)ctrl)->pitch;
Carlos Lopez a09598
        duration = ((BellCtrl*)ctrl)->duration;
Carlos Lopez a09598
    } else if (class == KbdFeedbackClass) {
Carlos Lopez a09598
        pitch = ((KeybdCtrl*)ctrl)->bell_pitch;
Carlos Lopez a09598
        duration = ((KeybdCtrl*)ctrl)->bell_duration;    
Carlos Lopez a09598
    } else {
Carlos Lopez a09598
        ErrorF("QuartzBell: bad bell class %d\n", class);
Carlos Lopez a09598
        return;
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
    if (quartzUseSysBeep) {
Carlos Lopez a09598
        if (volume)
Carlos Lopez a09598
            NSBeep();
Carlos Lopez a09598
    } else {
Carlos Lopez a09598
        QuartzCoreAudioBell(volume, pitch, duration);
Carlos Lopez a09598
    }
Carlos Lopez a09598
}
Carlos Lopez a09598
Carlos Lopez a09598
Carlos Lopez a09598
/*
Carlos Lopez a09598
 * QuartzAudioInit
Carlos Lopez a09598
 *  Prepare to play the bell with the CoreAudio API
Carlos Lopez a09598
 */
Carlos Lopez a09598
void QuartzAudioInit(void) 
Carlos Lopez a09598
{
Carlos Lopez a09598
    UInt32 propertySize;
Carlos Lopez a09598
    OSStatus status;
Carlos Lopez a09598
    AudioDeviceID outputDevice;
Carlos Lopez a09598
    AudioStreamBasicDescription outputStreamDescription;
Carlos Lopez a09598
    double sampleRate;
Carlos Lopez a09598
Carlos Lopez a09598
    // Get the default output device
Carlos Lopez a09598
    propertySize = sizeof(outputDevice);
Carlos Lopez a09598
    status = AudioHardwareGetProperty(
Carlos Lopez a09598
                    kAudioHardwarePropertyDefaultOutputDevice, 
Carlos Lopez a09598
                    &propertySize, &outputDevice);
Carlos Lopez a09598
    if (status) {
Carlos Lopez a09598
        ErrorF("QuartzAudioInit: AudioHardwareGetProperty returned %d\n",
Carlos Lopez a09598
               status);
Carlos Lopez a09598
        return;
Carlos Lopez a09598
    }
Carlos Lopez a09598
    if (outputDevice == kAudioDeviceUnknown) {
Carlos Lopez a09598
        ErrorF("QuartzAudioInit: No audio output devices available.\n");
Carlos Lopez a09598
        return;
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
    // Get the basic device description
Carlos Lopez a09598
    propertySize = sizeof(outputStreamDescription);
Carlos Lopez a09598
    status = AudioDeviceGetProperty(outputDevice, 0, FALSE, 
Carlos Lopez a09598
                                    kAudioDevicePropertyStreamFormat, 
Carlos Lopez a09598
                                    &propertySize, &outputStreamDescription);
Carlos Lopez a09598
    if (status) {
Carlos Lopez a09598
        ErrorF("QuartzAudioInit: GetProperty(stream format) returned %d\n",
Carlos Lopez a09598
               status);
Carlos Lopez a09598
        return;
Carlos Lopez a09598
    }
Carlos Lopez a09598
    sampleRate = outputStreamDescription.mSampleRate;
Carlos Lopez a09598
Carlos Lopez a09598
    // Fill in the playback data
Carlos Lopez a09598
    data.frequency = 0;
Carlos Lopez a09598
    data.amplitude = 0;
Carlos Lopez a09598
    data.curFrame = 0;
Carlos Lopez a09598
    data.remainingFrames = 0; 
Carlos Lopez a09598
    data.bytesPerFrame = outputStreamDescription.mBytesPerFrame;
Carlos Lopez a09598
    data.sampleRate = sampleRate;
Carlos Lopez a09598
    // data.bufferByteCount = bufferByteCount;
Carlos Lopez a09598
    data.playing = FALSE;
Carlos Lopez a09598
    data.prevAmplitude = 0;
Carlos Lopez a09598
    data.prevFrame = 0;
Carlos Lopez a09598
    data.prevFrequency = 0;
Carlos Lopez a09598
    data.fadeLength = data.sampleRate / 200;
Carlos Lopez a09598
    pthread_mutex_init(&data.lock, NULL); // fixme error check
Carlos Lopez a09598
Carlos Lopez a09598
    // fixme assert fadeLength
Carlos Lopez a09598
Carlos Lopez a09598
    // Prepare for playback
Carlos Lopez a09598
    status = AudioDeviceAddIOProc(outputDevice, QuartzAudioIOProc, &data);
Carlos Lopez a09598
    if (status) {
Carlos Lopez a09598
        ErrorF("QuartzAudioInit: AddIOProc returned %d\n", status);
Carlos Lopez a09598
        return;
Carlos Lopez a09598
    }
Carlos Lopez a09598
Carlos Lopez a09598
    // success!
Carlos Lopez a09598
    quartzAudioDevice = outputDevice;
Carlos Lopez a09598
}