First import
[xorg_rtime.git] / xorg-server-1.4 / hw / darwin / quartz / quartzAudio.c
blob16b9c2ba8aeeb9e3cd36248594499bceea3c7080
1 //
2 // QuartzAudio.m
3 //
4 // X Window bell support using CoreAudio or AppKit.
5 // Greg Parker gparker@cs.stanford.edu 19 Feb 2001
6 //
7 // Info about sine wave sound playback:
8 // CoreAudio code derived from macosx-dev posting by Tim Wood
9 // http://www.omnigroup.com/mailman/archive/macosx-dev/2000-May/002004.html
10 // Smoothing transitions between sounds
11 // http://www.wam.umd.edu/~mphoenix/dss/dss.html
14 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
16 * Permission is hereby granted, free of charge, to any person obtaining a
17 * copy of this software and associated documentation files (the "Software"),
18 * to deal in the Software without restriction, including without limitation
19 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 * and/or sell copies of the Software, and to permit persons to whom the
21 * Software is furnished to do so, subject to the following conditions:
23 * The above copyright notice and this permission notice shall be included in
24 * all copies or substantial portions of the Software.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
30 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
31 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 * DEALINGS IN THE SOFTWARE.
34 * Except as contained in this notice, the name(s) of the above copyright
35 * holders shall not be used in advertising or otherwise to promote the sale,
36 * use or other dealings in this Software without prior written authorization.
38 #ifdef HAVE_XORG_CONFIG_H
39 #include <xorg-config.h>
40 #endif
41 #include "quartzCommon.h"
42 #include "quartzAudio.h"
44 #include <CoreAudio/CoreAudio.h>
45 #include <pthread.h>
47 #include "inputstr.h"
48 #include <X11/extensions/XI.h>
49 #include <assert.h>
51 void NSBeep();
53 typedef struct QuartzAudioRec {
54 double frequency;
55 double amplitude;
57 UInt32 curFrame;
58 UInt32 remainingFrames;
59 UInt32 totalFrames;
60 UInt32 bytesPerFrame;
61 double sampleRate;
62 UInt32 fadeLength;
64 UInt32 bufferByteCount;
65 Boolean playing;
66 pthread_mutex_t lock;
68 // used to fade out interrupted sound and avoid 'pop'
69 double prevFrequency;
70 double prevAmplitude;
71 UInt32 prevFrame;
72 } QuartzAudioRec;
74 static AudioDeviceID quartzAudioDevice = kAudioDeviceUnknown;
75 static QuartzAudioRec data;
79 * QuartzAudioEnvelope
80 * Fade sound in and out to avoid pop.
81 * Sounds with shorter duration will never reach full amplitude. Deal.
83 static double QuartzAudioEnvelope(
84 UInt32 curFrame,
85 UInt32 totalFrames,
86 UInt32 fadeLength )
88 double fadeFrames = min(fadeLength, totalFrames / 2);
89 if (fadeFrames < 1) return 0;
91 if (curFrame < fadeFrames) {
92 return curFrame / fadeFrames;
93 } else if (curFrame > totalFrames - fadeFrames) {
94 return (totalFrames-curFrame) / fadeFrames;
95 } else {
96 return 1.0;
102 * QuartzFillBuffer
103 * Fill this buffer with data and update the data position.
104 * FIXME: this is ugly
106 static void QuartzFillBuffer(
107 AudioBuffer *audiobuffer,
108 QuartzAudioRec *data )
110 float *buffer, *b;
111 unsigned int frame, frameCount;
112 unsigned int bufferFrameCount;
113 float multiplier, v;
114 int i;
116 buffer = (float *)audiobuffer->mData;
117 bufferFrameCount = audiobuffer->mDataByteSize / data->bytesPerFrame;
119 frameCount = min(bufferFrameCount, data->remainingFrames);
121 // Fade out previous sine wave, if any.
122 b = buffer;
123 if (data->prevFrame) {
124 multiplier = 2*M_PI*(data->prevFrequency/data->sampleRate);
125 for (frame = 0; frame < data->fadeLength; frame++) {
126 v = data->prevAmplitude *
127 QuartzAudioEnvelope(frame+data->fadeLength,
128 2*data->fadeLength,
129 data->fadeLength) *
130 sin(multiplier * (data->prevFrame+frame));
131 for (i = 0; i < audiobuffer->mNumberChannels; i++) {
132 *b++ = v;
135 // no more prev fade
136 data->prevFrame = 0;
138 // adjust for space eaten by prev fade
139 buffer += audiobuffer->mNumberChannels*frame;
140 bufferFrameCount -= frame;
141 frameCount = min(bufferFrameCount, data->remainingFrames);
144 // Write a sine wave with the specified frequency and amplitude
145 multiplier = 2*M_PI*(data->frequency/data->sampleRate);
146 for (frame = 0; frame < frameCount; frame++) {
147 v = data->amplitude *
148 QuartzAudioEnvelope(data->curFrame+frame, data->totalFrames,
149 data->fadeLength) *
150 sin(multiplier * (data->curFrame+frame));
151 for (i = 0; i < audiobuffer->mNumberChannels; i++) {
152 *b++ = v;
156 // Zero out the rest of the buffer, if any
157 memset(b, 0, sizeof(float) * audiobuffer->mNumberChannels *
158 (bufferFrameCount-frame));
160 data->curFrame += frameCount;
161 data->remainingFrames -= frameCount;
162 if (data->remainingFrames == 0) {
163 data->playing = FALSE;
164 data->curFrame = 0;
170 * QuartzAudioIOProc
171 * Callback function for audio playback.
172 * FIXME: use inOutputTime to correct for skipping
174 static OSStatus
175 QuartzAudioIOProc(
176 AudioDeviceID inDevice,
177 const AudioTimeStamp *inNow,
178 const AudioBufferList *inInputData,
179 const AudioTimeStamp *inInputTime,
180 AudioBufferList *outOutputData,
181 const AudioTimeStamp *inOutputTime,
182 void *inClientData )
184 QuartzAudioRec *data = (QuartzAudioRec *)inClientData;
185 int i;
186 Boolean wasPlaying;
188 pthread_mutex_lock(&data->lock);
189 wasPlaying = data->playing;
190 for (i = 0; i < outOutputData->mNumberBuffers; i++) {
191 if (data->playing) {
192 QuartzFillBuffer(outOutputData->mBuffers+i, data);
194 else {
195 memset(outOutputData->mBuffers[i].mData, 0,
196 outOutputData->mBuffers[i].mDataByteSize);
199 if (wasPlaying && !data->playing) {
200 OSStatus err;
201 err = AudioDeviceStop(inDevice, QuartzAudioIOProc);
203 pthread_mutex_unlock(&data->lock);
204 return 0;
209 * QuartzCoreAudioBell
210 * Play a tone using the CoreAudio API
212 static void QuartzCoreAudioBell(
213 int volume, // volume is % of max
214 int pitch, // pitch is Hz
215 int duration ) // duration is milliseconds
217 if (quartzAudioDevice == kAudioDeviceUnknown) return;
219 pthread_mutex_lock(&data.lock);
221 // fade previous sound, if any
222 data.prevFrequency = data.frequency;
223 data.prevAmplitude = data.amplitude;
224 data.prevFrame = data.curFrame;
226 // set new sound
227 data.frequency = pitch;
228 data.amplitude = volume / 100.0;
229 data.curFrame = 0;
230 data.totalFrames = (int)(data.sampleRate * duration / 1000.0);
231 data.remainingFrames = data.totalFrames;
233 if (! data.playing) {
234 OSStatus status;
235 status = AudioDeviceStart(quartzAudioDevice, QuartzAudioIOProc);
236 if (status) {
237 ErrorF("QuartzAudioBell: AudioDeviceStart returned %ld\n", status);
238 } else {
239 data.playing = TRUE;
242 pthread_mutex_unlock(&data.lock);
247 * DarwinModeBell
248 * Ring the bell
250 void DarwinModeBell(
251 int volume, // volume in percent of max
252 DeviceIntPtr pDevice,
253 pointer ctrl,
254 int class )
256 int pitch; // pitch in Hz
257 int duration; // duration in milliseconds
259 if (class == BellFeedbackClass) {
260 pitch = ((BellCtrl*)ctrl)->pitch;
261 duration = ((BellCtrl*)ctrl)->duration;
262 } else if (class == KbdFeedbackClass) {
263 pitch = ((KeybdCtrl*)ctrl)->bell_pitch;
264 duration = ((KeybdCtrl*)ctrl)->bell_duration;
265 } else {
266 ErrorF("QuartzBell: bad bell class %d\n", class);
267 return;
270 if (quartzUseSysBeep) {
271 if (volume)
272 NSBeep();
273 } else {
274 QuartzCoreAudioBell(volume, pitch, duration);
280 * QuartzAudioInit
281 * Prepare to play the bell with the CoreAudio API
283 void QuartzAudioInit(void)
285 UInt32 propertySize;
286 OSStatus status;
287 AudioDeviceID outputDevice;
288 AudioStreamBasicDescription outputStreamDescription;
289 double sampleRate;
291 // Get the default output device
292 propertySize = sizeof(outputDevice);
293 status = AudioHardwareGetProperty(
294 kAudioHardwarePropertyDefaultOutputDevice,
295 &propertySize, &outputDevice);
296 if (status) {
297 ErrorF("QuartzAudioInit: AudioHardwareGetProperty returned %ld\n",
298 status);
299 return;
301 if (outputDevice == kAudioDeviceUnknown) {
302 ErrorF("QuartzAudioInit: No audio output devices available.\n");
303 return;
306 // Get the basic device description
307 propertySize = sizeof(outputStreamDescription);
308 status = AudioDeviceGetProperty(outputDevice, 0, FALSE,
309 kAudioDevicePropertyStreamFormat,
310 &propertySize, &outputStreamDescription);
311 if (status) {
312 ErrorF("QuartzAudioInit: GetProperty(stream format) returned %ld\n",
313 status);
314 return;
316 sampleRate = outputStreamDescription.mSampleRate;
318 // Fill in the playback data
319 data.frequency = 0;
320 data.amplitude = 0;
321 data.curFrame = 0;
322 data.remainingFrames = 0;
323 data.bytesPerFrame = outputStreamDescription.mBytesPerFrame;
324 data.sampleRate = sampleRate;
325 // data.bufferByteCount = bufferByteCount;
326 data.playing = FALSE;
327 data.prevAmplitude = 0;
328 data.prevFrame = 0;
329 data.prevFrequency = 0;
330 data.fadeLength = data.sampleRate / 200;
331 pthread_mutex_init(&data.lock, NULL); // fixme error check
333 // fixme assert fadeLength<framesPerBuffer
335 // Prepare for playback
336 status = AudioDeviceAddIOProc(outputDevice, QuartzAudioIOProc, &data);
337 if (status) {
338 ErrorF("QuartzAudioInit: AddIOProc returned %ld\n", status);
339 return;
342 // success!
343 quartzAudioDevice = outputDevice;