2 * Copyright (c) 2006-2009 Ed Schouten <ed@80386.nl>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
27 * @file audio_output_coreaudio.c
28 * @brief Apple CoreAudio audio output driver.
33 #include <CoreAudio/AudioHardware.h>
35 #include "audio_file.h"
36 #include "audio_output.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.
53 * @brief The audio device ID obtained from CoreAudio.
57 * @brief The stream format used by the CoreAudio device.
59 AudioStreamBasicDescription afmt
;
60 #ifdef MAC_OS_X_VERSION_10_5
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 */
70 * @brief The buffer that will be played when the current buffer is
71 * finished processing.
75 * @brief The buffer that is currently processed by CoreAudio.
79 * @brief The length of the buffers used by CoreAudio.
83 * @brief The length of the abufcur that is currently used. It should
84 * only be used with g_atomic_* operations.
88 * @brief A not really used mutex that is used for the conditional
93 * @brief The conditional variable that is used to inform the
94 * application that a buffer has been processed.
99 * @brief Preferred audio channels used for audio playback. We use it to
102 UInt32 achans
[2] = { 0, 0 };
103 #endif /* BUILD_VOLUME */
106 * @brief Pull-function needed by CoreAudio to copy data to the audio
110 audio_output_ioproc(AudioDeviceID inDevice
, const AudioTimeStamp
*inNow
,
111 const AudioBufferList
*inInputData
, const AudioTimeStamp
*inInputTime
,
112 AudioBufferList
*outOutputData
, const AudioTimeStamp
*inOutputTime
,
116 float *ob
= outOutputData
->mBuffers
[0].mData
;
118 /* Stop the IOProc handling if we're going idle */
119 len
= g_atomic_int_get(&abufulen
);
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
++)
139 audio_output_open(void)
143 /* Obtain the audio device ID */
145 if (AudioHardwareGetProperty(
146 kAudioHardwarePropertyDefaultOutputDevice
,
147 &size
, &adid
) != 0 || adid
== kAudioDeviceUnknown
)
150 /* Adjust the stream format */
152 if (AudioDeviceGetProperty(adid
, 0, false,
153 kAudioDevicePropertyStreamFormat
, &size
, &afmt
) != 0 ||
154 afmt
.mFormatID
!= kAudioFormatLinearPCM
)
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
;
166 AudioDeviceSetProperty(adid
, 0, 0, false,
167 kAudioDevicePropertyBufferSize
, size
, &abuflen
);
168 if (AudioDeviceGetProperty(adid
, 0, false,
169 kAudioDevicePropertyBufferSize
, &size
, &abuflen
) != 0)
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
,
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 */
199 g_printerr("%s\n", _("Cannot open the audio device."));
204 audio_output_play(struct audio_file
*fd
)
209 /* Read data in our temporary buffer */
210 if ((len
= audio_file_read(fd
, abufnew
, abuflen
)) == 0)
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 */
223 AudioDeviceGetProperty(adid
, 0, false,
224 kAudioDevicePropertyStreamFormat
, &size
, &afmt
);
226 gui_msgbar_warn(_("Sample rate or amount of channels not supported."));
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 */
242 /* Atomically set the usage length */
243 g_atomic_int_set(&abufulen
, len
);
245 /* Start processing of the data */
246 AudioDeviceStart(adid
, aprocid
);
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 */
267 * @brief Adjust the audio output by a certain ratio and return the new
271 audio_output_volume_adjust(Float32 n
)
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)
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)
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 */