Added anchor, so actual song does not run away from window during playback.
[herrie-working.git] / herrie / src / audio_output_coreaudio.c
blob8eb9f7929df219a0fe935ff436229c9cd4824c4c
1 /*
2 * Copyright (c) 2006-2011 Ed Schouten <ed@80386.nl>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 /**
27 * @file audio_output_coreaudio.c
28 * @brief Apple CoreAudio audio output driver.
31 #include "stdinc.h"
33 #include <CoreAudio/AudioHardware.h>
35 #include "audio_file.h"
36 #include "audio_output.h"
37 #include "gui.h"
40 * A small problem with CoreAudio is that it doesn't actually have a
41 * standard blocking push-interface such as OSS or ALSA. We have to
42 * install a routine in the audio layer that gets called routinely to
43 * pull data from buffers.
45 * According to a discussion on the CoreAudio mailing list, we can't use
46 * stuff such as spinlocks from within this callback routine (probably
47 * served through some kind of interrupt-driven context) we guarantee
48 * buffer locking by using atomic operations on abufulen; the indicator
49 * of the length of the buffer that is used.
52 /**
53 * @brief The audio device ID obtained from CoreAudio.
55 AudioDeviceID adid;
56 /**
57 * @brief The stream format used by the CoreAudio device.
59 AudioStreamBasicDescription afmt;
60 #ifdef MAC_OS_X_VERSION_10_5
61 /**
62 * @brief The IO-process ID.
64 AudioDeviceIOProcID aprocid;
65 #else /* !MAC_OS_X_VERSION_10_5 */
66 #define aprocid audio_output_ioproc
67 #endif /* MAC_OS_X_VERSION_10_5 */
69 /**
70 * @brief The buffer that will be played when the current buffer is
71 * finished processing.
73 int16_t *abufnew;
74 /**
75 * @brief The buffer that is currently processed by CoreAudio.
77 int16_t *abufcur;
78 /**
79 * @brief The length of the buffers used by CoreAudio.
81 int abuflen;
82 /**
83 * @brief The length of the abufcur that is currently used. It should
84 * only be used with g_atomic_* operations.
86 int abufulen = 0;
87 /**
88 * @brief A not really used mutex that is used for the conditional
89 * variable.
91 GMutex *abuflock;
92 /**
93 * @brief The conditional variable that is used to inform the
94 * application that a buffer has been processed.
96 GCond *abufdrained;
97 #ifdef BUILD_VOLUME
98 /**
99 * @brief Preferred audio channels used for audio playback. We use it to
100 * set the volume.
102 UInt32 achans[2] = { 0, 0 };
103 #endif /* BUILD_VOLUME */
106 * @brief Pull-function needed by CoreAudio to copy data to the audio
107 * buffers.
109 static OSStatus
110 audio_output_ioproc(AudioDeviceID inDevice, const AudioTimeStamp *inNow,
111 const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime,
112 AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime,
113 void *inClientData)
115 int i, len;
116 float *ob = outOutputData->mBuffers[0].mData;
118 /* Stop the IOProc handling if we're going idle */
119 len = g_atomic_int_get(&abufulen);
120 if (len == 0)
121 AudioDeviceStop(adid, aprocid);
123 /* Convert the data to floats */
124 for (i = 0; i < len; i++)
125 ob[i] = (float)abufcur[i] / SHRT_MAX;
127 /* Empty the buffer and notify that we can receive new data */
128 g_atomic_int_set(&abufulen, 0);
129 g_cond_signal(abufdrained);
131 /* Fill the trailer with zero's */
132 for (; i < abuflen; i++)
133 ob[i] = 0.0;
135 return (0);
139 audio_output_open(void)
141 UInt32 size;
143 /* Obtain the audio device ID */
144 size = sizeof adid;
145 if (AudioHardwareGetProperty(
146 kAudioHardwarePropertyDefaultOutputDevice,
147 &size, &adid) != 0 || adid == kAudioDeviceUnknown)
148 goto error;
150 /* Adjust the stream format */
151 size = sizeof afmt;
152 if (AudioDeviceGetProperty(adid, 0, false,
153 kAudioDevicePropertyStreamFormat, &size, &afmt) != 0 ||
154 afmt.mFormatID != kAudioFormatLinearPCM)
155 goto error;
157 /* To be set on the first run */
158 afmt.mSampleRate = 0;
159 afmt.mChannelsPerFrame = 0;
160 afmt.mBytesPerFrame = afmt.mChannelsPerFrame * sizeof (float);
161 afmt.mBytesPerPacket = afmt.mBytesPerFrame * afmt.mFramesPerPacket;
163 /* Decide what buffer size to use */
164 size = sizeof abuflen;
165 abuflen = 32768;
166 AudioDeviceSetProperty(adid, 0, 0, false,
167 kAudioDevicePropertyBufferSize, size, &abuflen);
168 if (AudioDeviceGetProperty(adid, 0, false,
169 kAudioDevicePropertyBufferSize, &size, &abuflen) != 0)
170 goto error;
172 #ifdef BUILD_VOLUME
173 /* Store the audio channels */
174 size = sizeof achans;
175 AudioDeviceGetProperty(adid, 0, false,
176 kAudioDevicePropertyPreferredChannelsForStereo, &size, &achans);
177 #endif /* BUILD_VOLUME */
179 /* The buffer size reported is in floats */
180 abuflen /= sizeof(float);
181 abufnew = g_malloc(abuflen * sizeof(int16_t));
182 abufcur = g_malloc(abuflen * sizeof(int16_t));
184 /* Locking down the buffer length */
185 abuflock = g_mutex_new();
186 abufdrained = g_cond_new();
188 /* Add our own I/O handling routine */
189 #ifdef MAC_OS_X_VERSION_10_5
190 if (AudioDeviceCreateIOProcID(adid, audio_output_ioproc, NULL,
191 &aprocid) != 0)
192 #else /* !MAC_OS_X_VERSION_10_5 */
193 if (AudioDeviceAddIOProc(adid, audio_output_ioproc, NULL) != 0)
194 #endif /* MAC_OS_X_VERSION_10_5 */
195 goto error;
197 return (0);
198 error:
199 g_printerr("%s\n", _("Cannot open the audio device."));
200 return (-1);
204 audio_output_play(struct audio_file *fd)
206 UInt32 len, size;
207 int16_t *tmp;
209 /* Read data in our temporary buffer */
210 if ((len = audio_file_read(fd, abufnew, abuflen)) == 0)
211 return (-1);
213 if (fd->srate != afmt.mSampleRate ||
214 fd->channels != afmt.mChannelsPerFrame) {
215 /* Sample rate or the amount of channels has changed */
216 afmt.mSampleRate = fd->srate;
217 afmt.mChannelsPerFrame = fd->channels;
219 if (AudioDeviceSetProperty(adid, 0, 0, 0,
220 kAudioDevicePropertyStreamFormat, sizeof afmt, &afmt) != 0) {
221 /* Get current settings back */
222 size = sizeof afmt;
223 AudioDeviceGetProperty(adid, 0, false,
224 kAudioDevicePropertyStreamFormat, &size, &afmt);
226 gui_msgbar_warn(_("Sample rate or amount of channels not supported."));
227 return (-1);
231 /* XXX: Mutex not actually needed - only for the condvar */
232 g_mutex_lock(abuflock);
233 while (g_atomic_int_get(&abufulen) != 0)
234 g_cond_wait(abufdrained, abuflock);
235 g_mutex_unlock(abuflock);
237 /* Toggle the buffers */
238 tmp = abufcur;
239 abufcur = abufnew;
240 abufnew = tmp;
242 /* Atomically set the usage length */
243 g_atomic_int_set(&abufulen, len);
245 /* Start processing of the data */
246 AudioDeviceStart(adid, aprocid);
248 return (0);
251 void
252 audio_output_close(void)
254 AudioDeviceStop(adid, aprocid);
255 #ifdef MAC_OS_X_VERSION_10_5
256 AudioDeviceDestroyIOProcID(adid, aprocid);
257 #else /* !MAC_OS_X_VERSION_10_5 */
258 AudioDeviceRemoveIOProc(adid, aprocid);
259 #endif /* MAC_OS_X_VERSION_10_5 */
261 g_free(abufnew);
262 g_free(abufcur);
265 #ifdef BUILD_VOLUME
267 * @brief Adjust the audio output by a certain ratio and return the new
268 * value.
270 static int
271 audio_output_volume_adjust(Float32 n)
273 Float32 vl, vr, vn;
274 UInt32 size = sizeof vl;
277 * Merge left and right. On Mac OS X, we want to do this. My
278 * PowerBook has this odd bug that the built-in OS X volume
279 * applet makes the sound card go unbalanced after a long amount
280 * of time. We can prevent that over here...
282 if (AudioDeviceGetProperty(adid, achans[0], false,
283 kAudioDevicePropertyVolumeScalar, &size, &vl) != 0 ||
284 AudioDeviceGetProperty(adid, achans[1], false,
285 kAudioDevicePropertyVolumeScalar, &size, &vr) != 0)
286 return (-1);
287 vn = CLAMP((vl + vr) / 2.0 + n, 0.0, 1.0);
289 /* Set the new volume */
290 if (AudioDeviceSetProperty(adid, 0, achans[0], false,
291 kAudioDevicePropertyVolumeScalar, size, &vn) != 0 ||
292 AudioDeviceSetProperty(adid, 0, achans[1], false,
293 kAudioDevicePropertyVolumeScalar, size, &vn) != 0)
294 return (-1);
296 return (vn * 100.0);
300 audio_output_volume_up(void)
302 return audio_output_volume_adjust(0.04);
306 audio_output_volume_down(void)
308 return audio_output_volume_adjust(-0.04);
310 #endif /* BUILD_VOLUME */