setupapi: Fix memory leak.
[wine/hacks.git] / dlls / winecoreaudio.drv / audiounit.c
blobbef882b346ccfadac1f782401c99903b589c721a
1 /*
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
21 #include "config.h"
23 #ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H
25 #define ULONG CoreFoundation_ULONG
26 #define HRESULT CoreFoundation_HRESULT
27 #include <CoreServices/CoreServices.h>
28 #include <AudioUnit/AudioUnit.h>
29 #include <AudioToolbox/AudioToolbox.h>
30 #undef ULONG
31 #undef HRESULT
33 #undef DPRINTF
34 #undef STDMETHODCALLTYPE
35 #include "coreaudio.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(wave);
39 WINE_DECLARE_DEBUG_CHANNEL(midi);
41 static const char *streamDescription(const AudioStreamBasicDescription* stream)
43 return wine_dbg_sprintf("\n mSampleRate : %f\n mFormatID : %s\n mFormatFlags : %lX\n mBytesPerPacket : %lu\n mFramesPerPacket : %lu\n mBytesPerFrame : %lu\n mChannelsPerFrame : %lu\n mBitsPerChannel : %lu\n",
44 stream->mSampleRate,
45 wine_dbgstr_fourcc(stream->mFormatID),
46 stream->mFormatFlags,
47 stream->mBytesPerPacket,
48 stream->mFramesPerPacket,
49 stream->mBytesPerFrame,
50 stream->mChannelsPerFrame,
51 stream->mBitsPerChannel);
54 extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon,
55 AudioUnitRenderActionFlags *ioActionFlags,
56 const AudioTimeStamp *inTimeStamp,
57 UInt32 inBusNumber,
58 UInt32 inNumberFrames,
59 AudioBufferList *ioData);
61 extern OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
62 AudioUnitRenderActionFlags *ioActionFlags,
63 const AudioTimeStamp *inTimeStamp,
64 UInt32 inBusNumber,
65 UInt32 inNumberFrames,
66 AudioBufferList *ioData);
68 int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au)
70 OSStatus err;
71 Component comp;
72 ComponentDescription desc;
73 AURenderCallbackStruct callbackStruct;
75 TRACE("\n");
77 desc.componentType = kAudioUnitType_Output;
78 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
79 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
80 desc.componentFlags = 0;
81 desc.componentFlagsMask = 0;
83 comp = FindNextComponent(NULL, &desc);
84 if (comp == NULL)
85 return 0;
87 err = OpenAComponent(comp, au);
88 if (err != noErr || *au == NULL)
89 return 0;
91 callbackStruct.inputProc = CoreAudio_woAudioUnitIOProc;
92 callbackStruct.inputProcRefCon = wwo;
94 err = AudioUnitSetProperty( *au,
95 kAudioUnitProperty_SetRenderCallback,
96 kAudioUnitScope_Input,
97 0,
98 &callbackStruct,
99 sizeof(callbackStruct));
100 return (err == noErr);
103 int AudioUnit_CloseAudioUnit(AudioUnit au)
105 OSStatus err = CloseComponent(au);
106 return (err == noErr);
109 int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *stream)
111 OSStatus err = noErr;
113 TRACE("input format: %s\n", streamDescription(stream));
115 err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
116 0, stream, sizeof(*stream));
118 if (err != noErr)
120 ERR("AudioUnitSetProperty return an error %s\n", wine_dbgstr_fourcc(err));
121 return 0;
124 err = AudioUnitInitialize(au);
125 if (err != noErr)
127 ERR("AudioUnitInitialize return an error %s\n", wine_dbgstr_fourcc(err));
128 return 0;
130 return 1;
133 int AudioUnit_SetVolume(AudioUnit au, float left, float right)
135 OSStatus err = noErr;
136 static int once;
138 if (!once++) FIXME("independent left/right volume not implemented (%f, %f)\n", left, right);
140 err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0);
142 if (err != noErr)
144 ERR("AudioUnitSetParameter return an error %s\n", wine_dbgstr_fourcc(err));
145 return 0;
147 return 1;
150 int AudioUnit_GetVolume(AudioUnit au, float *left, float *right)
152 OSStatus err = noErr;
153 static int once;
155 if (!once++) FIXME("independent left/right volume not implemented\n");
157 err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left);
158 if (err != noErr)
160 ERR("AudioUnitGetParameter return an error %s\n", wine_dbgstr_fourcc(err));
161 return 0;
163 *right = *left;
164 return 1;
168 /* FIXME: implement sample rate conversion on input */
169 int AudioUnit_GetInputDeviceSampleRate(void)
171 AudioDeviceID defaultInputDevice;
172 UInt32 param;
173 Float64 sampleRate;
174 OSStatus err;
176 param = sizeof(defaultInputDevice);
177 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
178 if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
180 ERR("Couldn't get the default audio input device ID: %08lx\n", err);
181 return 0;
184 param = sizeof(sampleRate);
185 err = AudioDeviceGetProperty(defaultInputDevice, 0, 1, kAudioDevicePropertyNominalSampleRate, &param, &sampleRate);
186 if (err != noErr)
188 ERR("Couldn't get the device sample rate: %08lx\n", err);
189 return 0;
192 return sampleRate;
196 int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
197 WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
198 UInt32* outFrameCount)
200 OSStatus err = noErr;
201 ComponentDescription description;
202 Component component;
203 AudioUnit au;
204 UInt32 param;
205 AURenderCallbackStruct callback;
206 AudioDeviceID defaultInputDevice;
207 AudioStreamBasicDescription desiredFormat;
210 if (!outFrameCount)
212 ERR("Invalid parameter\n");
213 return 0;
216 /* Open the AudioOutputUnit */
217 description.componentType = kAudioUnitType_Output;
218 description.componentSubType = kAudioUnitSubType_HALOutput;
219 description.componentManufacturer = kAudioUnitManufacturer_Apple;
220 description.componentFlags = 0;
221 description.componentFlagsMask = 0;
223 component = FindNextComponent(NULL, &description);
224 if (!component)
226 ERR("FindNextComponent(kAudioUnitSubType_HALOutput) failed\n");
227 return 0;
230 err = OpenAComponent(component, &au);
231 if (err != noErr || au == NULL)
233 ERR("OpenAComponent failed: %08lx\n", err);
234 return 0;
237 /* Configure the AudioOutputUnit */
238 /* The AUHAL has two buses (AKA elements). Bus 0 is output from the app
239 * to the device. Bus 1 is input from the device to the app. Each bus
240 * has two ends (AKA scopes). Data goes from the input scope to the
241 * output scope. The terminology is somewhat confusing because the terms
242 * "input" and "output" have two meanings. Here's a summary:
244 * Bus 0, input scope: refers to the source of data to be output as sound
245 * Bus 0, output scope: refers to the actual sound output device
246 * Bus 1, input scope: refers to the actual sound input device
247 * Bus 1, output scope: refers to the destination of data received by the input device
250 /* Enable input on the AUHAL */
251 param = 1;
252 err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &param, sizeof(param));
253 if (err != noErr)
255 ERR("Couldn't enable input on AUHAL: %08lx\n", err);
256 goto error;
259 /* Disable Output on the AUHAL */
260 param = 0;
261 err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
262 if (err != noErr)
264 ERR("Couldn't disable output on AUHAL: %08lx\n", err);
265 goto error;
268 /* Find the default input device */
269 param = sizeof(defaultInputDevice);
270 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
271 if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
273 ERR("Couldn't get the default audio device ID: %08lx\n", err);
274 goto error;
277 /* Set the current device to the default input device. */
278 err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &defaultInputDevice, sizeof(defaultInputDevice));
279 if (err != noErr)
281 ERR("Couldn't set current device of AUHAL to default input device: %08lx\n", err);
282 goto error;
285 /* Setup render callback */
286 /* This will be called when the AUHAL has input data. However, it won't
287 * be passed the data itself. The callback will have to all AudioUnitRender. */
288 callback.inputProc = CoreAudio_wiAudioUnitIOProc;
289 callback.inputProcRefCon = wwi;
290 err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
291 if (err != noErr)
293 ERR("Couldn't set input callback of AUHAL: %08lx\n", err);
294 goto error;
297 /* Setup the desired data format. */
298 /* FIXME: implement sample rate conversion on input. We shouldn't set
299 * the mSampleRate of this to the desired sample rate. We need to query
300 * the input device and use that. If they don't match, we need to set up
301 * an AUConverter to do the sample rate conversion on a separate thread. */
302 desiredFormat.mFormatID = kAudioFormatLinearPCM;
303 desiredFormat.mFormatFlags = kLinearPCMFormatFlagIsPacked;
304 if (wBitsPerSample != 8)
305 desiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
306 desiredFormat.mSampleRate = nSamplesPerSec;
307 desiredFormat.mChannelsPerFrame = nChannels;
308 desiredFormat.mFramesPerPacket = 1;
309 desiredFormat.mBitsPerChannel = wBitsPerSample;
310 desiredFormat.mBytesPerFrame = desiredFormat.mBitsPerChannel * desiredFormat.mChannelsPerFrame / 8;
311 desiredFormat.mBytesPerPacket = desiredFormat.mBytesPerFrame * desiredFormat.mFramesPerPacket;
313 /* Set the AudioOutputUnit output data format */
314 err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desiredFormat, sizeof(desiredFormat));
315 if (err != noErr)
317 ERR("Couldn't set desired input format of AUHAL: %08lx\n", err);
318 goto error;
321 /* Get the number of frames in the IO buffer(s) */
322 param = sizeof(*outFrameCount);
323 err = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, outFrameCount, &param);
324 if (err != noErr)
326 ERR("Failed to get audio sample size: %08lx\n", err);
327 goto error;
330 TRACE("Frame count: %lu\n", *outFrameCount);
332 /* Initialize the AU */
333 err = AudioUnitInitialize(au);
334 if (err != noErr)
336 ERR("Failed to initialize AU: %08lx\n", err);
337 goto error;
340 *out_au = au;
342 return 1;
344 error:
345 if (au)
346 CloseComponent(au);
347 return 0;
351 * MIDI Synth Unit
353 int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth)
355 OSStatus err;
356 ComponentDescription desc;
357 AUNode synthNode;
358 AUNode outNode;
360 err = NewAUGraph(graph);
361 if (err != noErr)
363 ERR_(midi)("NewAUGraph return %s\n", wine_dbgstr_fourcc(err));
364 return 0;
367 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
368 desc.componentFlags = 0;
369 desc.componentFlagsMask = 0;
371 /* create synth node */
372 desc.componentType = kAudioUnitType_MusicDevice;
373 desc.componentSubType = kAudioUnitSubType_DLSSynth;
375 err = AUGraphNewNode(*graph, &desc, 0, NULL, &synthNode);
376 if (err != noErr)
378 ERR_(midi)("AUGraphNewNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(err));
379 return 0;
382 /* create out node */
383 desc.componentType = kAudioUnitType_Output;
384 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
386 err = AUGraphNewNode(*graph, &desc, 0, NULL, &outNode);
387 if (err != noErr)
389 ERR_(midi)("AUGraphNewNode cannot create outNode %s\n", wine_dbgstr_fourcc(err));
390 return 0;
393 err = AUGraphOpen(*graph);
394 if (err != noErr)
396 ERR_(midi)("AUGraphOpen return %s\n", wine_dbgstr_fourcc(err));
397 return 0;
400 /* connecting the nodes */
401 err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0);
402 if (err != noErr)
404 ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n", wine_dbgstr_fourcc(err));
405 return 0;
408 /* Get the synth unit */
409 err = AUGraphGetNodeInfo(*graph, synthNode, 0, 0, 0, synth);
410 if (err != noErr)
412 ERR_(midi)("AUGraphGetNodeInfo return %s\n", wine_dbgstr_fourcc(err));
413 return 0;
416 return 1;
419 int SynthUnit_Initialize(AudioUnit synth, AUGraph graph)
421 OSStatus err = noErr;
423 err = AUGraphInitialize(graph);
424 if (err != noErr)
426 ERR_(midi)("AUGraphInitialize(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
427 return 0;
430 err = AUGraphStart(graph);
431 if (err != noErr)
433 ERR_(midi)("AUGraphStart(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
434 return 0;
437 return 1;
440 int SynthUnit_Close(AUGraph graph)
442 OSStatus err = noErr;
444 err = AUGraphStop(graph);
445 if (err != noErr)
447 ERR_(midi)("AUGraphStop(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
448 return 0;
451 err = DisposeAUGraph(graph);
452 if (err != noErr)
454 ERR_(midi)("DisposeAUGraph(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
455 return 0;
458 return 1;
461 #endif