2 * Wine Driver for CoreAudio / AudioUnit
4 * Copyright 2005, 2006 Emmanuel Maillard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio
);
26 #ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H
27 #include <AudioUnit/AudioUnit.h>
29 static const char *streamDescription(const AudioStreamBasicDescription
* stream
)
31 return wine_dbg_sprintf("\n mSampleRate : %f\n mFormatID : %c%c%c%c\n mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
33 (char) (stream
->mFormatID
>> 24),
34 (char) (stream
->mFormatID
>> 16),
35 (char) (stream
->mFormatID
>> 8),
36 (char) stream
->mFormatID
,
38 stream
->mBytesPerPacket
,
39 stream
->mFramesPerPacket
,
40 stream
->mBytesPerFrame
,
41 stream
->mChannelsPerFrame
,
42 stream
->mBitsPerChannel
);
45 extern OSStatus
CoreAudio_woAudioUnitIOProc(void *inRefCon
,
46 AudioUnitRenderActionFlags
*ioActionFlags
,
47 const AudioTimeStamp
*inTimeStamp
,
49 UInt32 inNumberFrames
,
50 AudioBufferList
*ioData
);
52 extern OSStatus
CoreAudio_wiAudioUnitIOProc(void *inRefCon
,
53 AudioUnitRenderActionFlags
*ioActionFlags
,
54 const AudioTimeStamp
*inTimeStamp
,
56 UInt32 inNumberFrames
,
57 AudioBufferList
*ioData
);
59 int AudioUnit_CreateDefaultAudioUnit(void *wwo
, AudioUnit
*au
)
63 ComponentDescription desc
;
64 AURenderCallbackStruct callbackStruct
;
68 desc
.componentType
= kAudioUnitType_Output
;
69 desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
70 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
71 desc
.componentFlags
= 0;
72 desc
.componentFlagsMask
= 0;
74 comp
= FindNextComponent(NULL
, &desc
);
78 err
= OpenAComponent(comp
, au
);
82 callbackStruct
.inputProc
= CoreAudio_woAudioUnitIOProc
;
83 callbackStruct
.inputProcRefCon
= wwo
;
85 err
= AudioUnitSetProperty( *au
,
86 kAudioUnitProperty_SetRenderCallback
,
87 kAudioUnitScope_Input
,
90 sizeof(callbackStruct
));
91 return (err
== noErr
);
94 int AudioUnit_CloseAudioUnit(AudioUnit au
)
96 OSStatus err
= CloseComponent(au
);
97 return (err
== noErr
);
100 int AudioUnit_InitializeWithStreamDescription(AudioUnit au
, AudioStreamBasicDescription
*stream
)
102 OSStatus err
= noErr
;
104 TRACE("%s\n", streamDescription(stream
));
106 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
,
107 0, stream
, sizeof(*stream
));
111 ERR("AudioUnitSetProperty return an error %c%c%c%c\n", (char) (err
>> 24), (char) (err
>> 16), (char) (err
>> 8), (char) err
);
115 err
= AudioUnitInitialize(au
);
118 ERR("AudioUnitInitialize return an error %c%c%c%c\n", (char) (err
>> 24), (char) (err
>> 16), (char) (err
>> 8), (char) err
);
124 int AudioUnit_SetVolume(AudioUnit au
, float left
, float right
)
126 OSStatus err
= noErr
;
127 FIXME("independent left/right volume not implemented (%f, %f)\n", left
, right
);
129 err
= AudioUnitSetParameter(au
, kHALOutputParam_Volume
, kAudioUnitParameterFlag_Output
, 0, left
, 0);
133 ERR("AudioUnitSetParameter return an error %c%c%c%c\n", (char) (err
>> 24), (char) (err
>> 16), (char) (err
>> 8), (char) err
);
139 int AudioUnit_GetVolume(AudioUnit au
, float *left
, float *right
)
141 OSStatus err
= noErr
;
142 FIXME("independent left/right volume not implemented\n");
144 err
= AudioUnitGetParameter(au
, kHALOutputParam_Volume
, kAudioUnitParameterFlag_Output
, 0, left
);
147 ERR("AudioUnitGetParameter return an error %c%c%c%c\n", (char) (err
>> 24), (char) (err
>> 16), (char) (err
>> 8), (char) err
);
155 /* FIXME: implement sample rate conversion on input */
156 int AudioUnit_GetInputDeviceSampleRate(void)
158 AudioDeviceID defaultInputDevice
;
163 param
= sizeof(defaultInputDevice
);
164 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, ¶m
, &defaultInputDevice
);
165 if (err
!= noErr
|| defaultInputDevice
== kAudioDeviceUnknown
)
167 ERR("Couldn't get the default audio input device ID: %08lx\n", err
);
171 param
= sizeof(sampleRate
);
172 err
= AudioDeviceGetProperty(defaultInputDevice
, 0, 1, kAudioDevicePropertyNominalSampleRate
, ¶m
, &sampleRate
);
175 ERR("Couldn't get the device sample rate: %08lx\n", err
);
183 int AudioUnit_CreateInputUnit(void* wwi
, AudioUnit
* out_au
,
184 WORD nChannels
, DWORD nSamplesPerSec
, WORD wBitsPerSample
,
185 UInt32
* outFrameCount
)
187 OSStatus err
= noErr
;
188 ComponentDescription description
;
192 AURenderCallbackStruct callback
;
193 AudioDeviceID defaultInputDevice
;
194 AudioStreamBasicDescription desiredFormat
;
199 ERR("Invalid parameter\n");
203 /* Open the AudioOutputUnit */
204 description
.componentType
= kAudioUnitType_Output
;
205 description
.componentSubType
= kAudioUnitSubType_HALOutput
;
206 description
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
207 description
.componentFlags
= 0;
208 description
.componentFlagsMask
= 0;
210 component
= FindNextComponent(NULL
, &description
);
213 ERR("FindNextComponent(kAudioUnitSubType_HALOutput) failed\n");
217 err
= OpenAComponent(component
, &au
);
218 if (err
!= noErr
|| au
== NULL
)
220 ERR("OpenAComponent failed: %08lx\n", err
);
224 /* Configure the AudioOutputUnit */
225 /* The AUHAL has two buses (AKA elements). Bus 0 is output from the app
226 * to the device. Bus 1 is input from the device to the app. Each bus
227 * has two ends (AKA scopes). Data goes from the input scope to the
228 * output scope. The terminology is somewhat confusing because the terms
229 * "input" and "output" have two meanings. Here's a summary:
231 * Bus 0, input scope: refers to the source of data to be output as sound
232 * Bus 0, output scope: refers to the actual sound output device
233 * Bus 1, input scope: refers to the actual sound input device
234 * Bus 1, output scope: refers to the destination of data received by the input device
237 /* Enable input on the AUHAL */
239 err
= AudioUnitSetProperty(au
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Input
, 1, ¶m
, sizeof(param
));
242 ERR("Couldn't enable input on AUHAL: %08lx\n", err
);
246 /* Disable Output on the AUHAL */
248 err
= AudioUnitSetProperty(au
, kAudioOutputUnitProperty_EnableIO
, kAudioUnitScope_Output
, 0, ¶m
, sizeof(param
));
251 ERR("Couldn't disable output on AUHAL: %08lx\n", err
);
255 /* Find the default input device */
256 param
= sizeof(defaultInputDevice
);
257 err
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, ¶m
, &defaultInputDevice
);
258 if (err
!= noErr
|| defaultInputDevice
== kAudioDeviceUnknown
)
260 ERR("Couldn't get the default audio device ID: %08lx\n", err
);
264 /* Set the current device to the default input device. */
265 err
= AudioUnitSetProperty(au
, kAudioOutputUnitProperty_CurrentDevice
, kAudioUnitScope_Global
, 0, &defaultInputDevice
, sizeof(defaultInputDevice
));
268 ERR("Couldn't set current device of AUHAL to default input device: %08lx\n", err
);
272 /* Setup render callback */
273 /* This will be called when the AUHAL has input data. However, it won't
274 * be passed the data itself. The callback will have to all AudioUnitRender. */
275 callback
.inputProc
= CoreAudio_wiAudioUnitIOProc
;
276 callback
.inputProcRefCon
= wwi
;
277 err
= AudioUnitSetProperty(au
, kAudioOutputUnitProperty_SetInputCallback
, kAudioUnitScope_Global
, 0, &callback
, sizeof(callback
));
280 ERR("Couldn't set input callback of AUHAL: %08lx\n", err
);
284 /* Setup the desired data format. */
285 /* FIXME: implement sample rate conversion on input. We shouldn't set
286 * the mSampleRate of this to the desired sample rate. We need to query
287 * the input device and use that. If they don't match, we need to set up
288 * an AUConverter to do the sample rate conversion on a separate thread. */
289 desiredFormat
.mFormatID
= kAudioFormatLinearPCM
;
290 desiredFormat
.mFormatFlags
= kLinearPCMFormatFlagIsPacked
;
291 if (wBitsPerSample
!= 8)
292 desiredFormat
.mFormatFlags
|= kLinearPCMFormatFlagIsSignedInteger
;
293 desiredFormat
.mSampleRate
= nSamplesPerSec
;
294 desiredFormat
.mChannelsPerFrame
= nChannels
;
295 desiredFormat
.mFramesPerPacket
= 1;
296 desiredFormat
.mBitsPerChannel
= wBitsPerSample
;
297 desiredFormat
.mBytesPerFrame
= desiredFormat
.mBitsPerChannel
* desiredFormat
.mChannelsPerFrame
/ 8;
298 desiredFormat
.mBytesPerPacket
= desiredFormat
.mBytesPerFrame
* desiredFormat
.mFramesPerPacket
;
300 /* Set the AudioOutputUnit output data format */
301 err
= AudioUnitSetProperty(au
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Output
, 1, &desiredFormat
, sizeof(desiredFormat
));
304 ERR("Couldn't set desired input format of AUHAL: %08lx\n", err
);
308 /* Get the number of frames in the IO buffer(s) */
309 param
= sizeof(*outFrameCount
);
310 err
= AudioUnitGetProperty(au
, kAudioDevicePropertyBufferFrameSize
, kAudioUnitScope_Global
, 0, outFrameCount
, ¶m
);
313 ERR("Failed to get audio sample size: %08lx\n", err
);
317 TRACE("Frame count: %lu\n", *outFrameCount
);
319 /* Initialize the AU */
320 err
= AudioUnitInitialize(au
);
323 ERR("Failed to initialize AU: %08lx\n", err
);