msdaps: Implement ISessionProperties_SetProperties proxy and stub.
[wine] / dlls / winecoreaudio.drv / audiounit.c
1 /*
2  * Wine Driver for CoreAudio / AudioUnit
3  *
4  * Copyright 2005, 2006 Emmanuel Maillard
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #ifdef HAVE_AUDIOUNIT_AUDIOUNIT_H
24
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
32
33 #undef DPRINTF
34 #undef STDMETHODCALLTYPE
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(wave);
38 WINE_DECLARE_DEBUG_CHANNEL(midi);
39
40 extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, 
41                                 AudioUnitRenderActionFlags *ioActionFlags, 
42                                 const AudioTimeStamp *inTimeStamp, 
43                                 UInt32 inBusNumber, 
44                                 UInt32 inNumberFrames, 
45                                 AudioBufferList *ioData);
46
47 extern OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
48                                 AudioUnitRenderActionFlags *ioActionFlags,
49                                 const AudioTimeStamp *inTimeStamp,
50                                 UInt32 inBusNumber,
51                                 UInt32 inNumberFrames,
52                                 AudioBufferList *ioData);
53
54 int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au)
55 {
56     OSStatus err;
57     Component comp;
58     ComponentDescription desc;
59     AURenderCallbackStruct callbackStruct;
60     
61     desc.componentType = kAudioUnitType_Output;
62     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
63     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
64     desc.componentFlags = 0;
65     desc.componentFlagsMask = 0;
66
67     comp = FindNextComponent(NULL, &desc);
68     if (comp == NULL)
69         return 0;
70     
71     err = OpenAComponent(comp, au);
72     if (comp == NULL)
73         return 0;
74         
75     callbackStruct.inputProc = CoreAudio_woAudioUnitIOProc;
76     callbackStruct.inputProcRefCon = wwo;
77
78     err = AudioUnitSetProperty( *au, 
79                                 kAudioUnitProperty_SetRenderCallback, 
80                                 kAudioUnitScope_Input,
81                                 0, 
82                                 &callbackStruct, 
83                                 sizeof(callbackStruct));
84     return (err == noErr);
85 }
86
87 int AudioUnit_CloseAudioUnit(AudioUnit au)
88 {
89     OSStatus err = CloseComponent(au);
90     return (err == noErr);
91 }
92
93 int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *stream)
94 {
95     OSStatus err = noErr;
96         
97     err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
98                                 0, stream, sizeof(*stream));
99
100     if (err != noErr)
101     {
102         ERR("AudioUnitSetProperty return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
103         return 0;
104     }
105     
106     err = AudioUnitInitialize(au);
107     if (err != noErr)
108     {
109         ERR("AudioUnitInitialize return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
110         return 0;
111     }
112     return 1;
113 }
114
115 int AudioUnit_SetVolume(AudioUnit au, float left, float right)
116 {
117     OSStatus err = noErr;
118    
119     err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0);
120                                 
121     if (err != noErr)
122     {
123         ERR("AudioUnitSetParameter return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
124         return 0;
125     }
126     return 1;
127 }
128
129 int AudioUnit_GetVolume(AudioUnit au, float *left, float *right)
130 {
131     OSStatus err = noErr;
132     
133     err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left);
134     if (err != noErr)
135     {
136         ERR("AudioUnitGetParameter return an error %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
137         return 0;
138     }
139     *right = *left;
140     return 1;
141 }
142
143
144 /* FIXME: implement sample rate conversion on input */
145 int AudioUnit_GetInputDeviceSampleRate(void)
146 {
147     AudioDeviceID               defaultInputDevice;
148     UInt32                      param;
149     Float64                     sampleRate;
150     OSStatus                    err;
151
152     param = sizeof(defaultInputDevice);
153     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
154     if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
155     {
156         ERR("Couldn't get the default audio input device ID: %08lx\n", err);
157         return 0;
158     }
159
160     param = sizeof(sampleRate);
161     err = AudioDeviceGetProperty(defaultInputDevice, 0, 1, kAudioDevicePropertyNominalSampleRate, &param, &sampleRate);
162     if (err != noErr)
163     {
164         ERR("Couldn't get the device sample rate: %08lx\n", err);
165         return 0;
166     }
167
168     return sampleRate;
169 }
170
171
172 int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
173         WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
174         UInt32* outFrameCount)
175 {
176     OSStatus                    err = noErr;
177     ComponentDescription        description;
178     Component                   component;
179     AudioUnit                   au;
180     UInt32                      param;
181     AURenderCallbackStruct      callback;
182     AudioDeviceID               defaultInputDevice;
183     AudioStreamBasicDescription desiredFormat;
184
185
186     if (!outFrameCount)
187     {
188         ERR("Invalid parameter\n");
189         return 0;
190     }
191
192     /* Open the AudioOutputUnit */
193     description.componentType           = kAudioUnitType_Output;
194     description.componentSubType        = kAudioUnitSubType_HALOutput;
195     description.componentManufacturer   = kAudioUnitManufacturer_Apple;
196     description.componentFlags          = 0;
197     description.componentFlagsMask      = 0;
198
199     component = FindNextComponent(NULL, &description);
200     if (!component)
201     {
202         ERR("FindNextComponent(kAudioUnitSubType_HALOutput) failed\n");
203         return 0;
204     }
205
206     err = OpenAComponent(component, &au);
207     if (err != noErr || au == NULL)
208     {
209         ERR("OpenAComponent failed: %08lx\n", err);
210         return 0;
211     }
212
213     /* Configure the AudioOutputUnit */
214     /* The AUHAL has two buses (AKA elements).  Bus 0 is output from the app
215      * to the device.  Bus 1 is input from the device to the app.  Each bus
216      * has two ends (AKA scopes).  Data goes from the input scope to the
217      * output scope.  The terminology is somewhat confusing because the terms
218      * "input" and "output" have two meanings.  Here's a summary:
219      *
220      *      Bus 0, input scope: refers to the source of data to be output as sound
221      *      Bus 0, output scope: refers to the actual sound output device
222      *      Bus 1, input scope: refers to the actual sound input device
223      *      Bus 1, output scope: refers to the destination of data received by the input device
224      */
225
226     /* Enable input on the AUHAL */
227     param = 1;
228     err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &param, sizeof(param));
229     if (err != noErr)
230     {
231         ERR("Couldn't enable input on AUHAL: %08lx\n", err);
232         goto error;
233     }
234
235     /* Disable Output on the AUHAL */
236     param = 0;
237     err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &param, sizeof(param));
238     if (err != noErr)
239     {
240         ERR("Couldn't disable output on AUHAL: %08lx\n", err);
241         goto error;
242     }
243
244     /* Find the default input device */
245     param = sizeof(defaultInputDevice);
246     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
247     if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
248     {
249         ERR("Couldn't get the default audio device ID: %08lx\n", err);
250         goto error;
251     }
252
253     /* Set the current device to the default input device. */
254     err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &defaultInputDevice, sizeof(defaultInputDevice));
255     if (err != noErr)
256     {
257         ERR("Couldn't set current device of AUHAL to default input device: %08lx\n", err);
258         goto error;
259     }
260
261     /* Setup render callback */
262     /* This will be called when the AUHAL has input data.  However, it won't
263      * be passed the data itself.  The callback will have to all AudioUnitRender. */
264     callback.inputProc = CoreAudio_wiAudioUnitIOProc;
265     callback.inputProcRefCon = wwi;
266     err = AudioUnitSetProperty(au, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback));
267     if (err != noErr)
268     {
269         ERR("Couldn't set input callback of AUHAL: %08lx\n", err);
270         goto error;
271     }
272
273     /* Setup the desired data format. */
274     /* FIXME: implement sample rate conversion on input.  We shouldn't set
275      * the mSampleRate of this to the desired sample rate.  We need to query
276      * the input device and use that.  If they don't match, we need to set up
277      * an AUConverter to do the sample rate conversion on a separate thread. */
278     desiredFormat.mFormatID         = kAudioFormatLinearPCM;
279     desiredFormat.mFormatFlags      = kLinearPCMFormatFlagIsPacked;
280     if (wBitsPerSample != 8)
281         desiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
282     desiredFormat.mSampleRate       = nSamplesPerSec;
283     desiredFormat.mChannelsPerFrame = nChannels;
284     desiredFormat.mFramesPerPacket  = 1;
285     desiredFormat.mBitsPerChannel   = wBitsPerSample;
286     desiredFormat.mBytesPerFrame    = desiredFormat.mBitsPerChannel * desiredFormat.mChannelsPerFrame / 8;
287     desiredFormat.mBytesPerPacket   = desiredFormat.mBytesPerFrame * desiredFormat.mFramesPerPacket;
288
289     /* Set the AudioOutputUnit output data format */
290     err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desiredFormat, sizeof(desiredFormat));
291     if (err != noErr)
292     {
293         ERR("Couldn't set desired input format of AUHAL: %08lx\n", err);
294         goto error;
295     }
296
297     /* Get the number of frames in the IO buffer(s) */
298     param = sizeof(*outFrameCount);
299     err = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, 0, outFrameCount, &param);
300     if (err != noErr)
301     {
302         ERR("Failed to get audio sample size: %08lx\n", err);
303         goto error;
304     }
305
306     TRACE("Frame count: %lu\n", *outFrameCount);
307
308     /* Initialize the AU */
309     err = AudioUnitInitialize(au);
310     if (err != noErr)
311     {
312         ERR("Failed to initialize AU: %08lx\n", err);
313         goto error;
314     }
315
316     *out_au = au;
317
318     return 1;
319
320 error:
321     if (au)
322         CloseComponent(au);
323     return 0;
324 }
325
326 /*
327  *  MIDI Synth Unit
328  */
329 int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth)
330 {
331     OSStatus err;
332     ComponentDescription desc;
333     AUNode synthNode;
334     AUNode outNode;
335
336     err = NewAUGraph(graph);
337     if (err != noErr)
338     {
339         ERR_(midi)("NewAUGraph return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
340         return 0;
341     }
342
343     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
344     desc.componentFlags = 0;
345     desc.componentFlagsMask = 0;
346
347     /* create synth node */
348     desc.componentType = kAudioUnitType_MusicDevice;
349     desc.componentSubType = kAudioUnitSubType_DLSSynth;
350
351     err = AUGraphNewNode(*graph, &desc, 0, NULL, &synthNode);
352     if (err != noErr)
353     {
354         ERR_(midi)("AUGraphNewNode cannot create synthNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
355         return 0;
356     }
357
358     /* create out node */
359     desc.componentType = kAudioUnitType_Output;
360     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
361
362     err = AUGraphNewNode(*graph, &desc, 0, NULL, &outNode);
363     if (err != noErr)
364     {
365         ERR_(midi)("AUGraphNewNode cannot create outNode %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
366         return 0;
367     }
368
369     err = AUGraphOpen(*graph);
370     if (err != noErr)
371     {
372         ERR_(midi)("AUGraphOpen return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
373         return 0;
374     }
375
376     /* connecting the nodes */
377     err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0);
378     if (err != noErr)
379     {
380         ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
381         return 0;
382     }
383
384     /* Get the synth unit */
385     err = AUGraphGetNodeInfo(*graph, synthNode, 0, 0, 0, synth);
386     if (err != noErr)
387     {
388         ERR_(midi)("AUGraphGetNodeInfo return %c%c%c%c\n", (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
389         return 0;
390     }
391
392     return 1;
393 }
394
395 int SynthUnit_Initialize(AudioUnit synth, AUGraph graph)
396 {
397     OSStatus err = noErr;
398
399     err = AUGraphInitialize(graph);
400     if (err != noErr)
401     {
402         ERR_(midi)("AUGraphInitialize(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
403         return 0;
404     }
405
406     err = AUGraphStart(graph);
407     if (err != noErr)
408     {
409         ERR_(midi)("AUGraphStart(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
410         return 0;
411     }
412
413     return 1;
414 }
415
416 int SynthUnit_Close(AUGraph graph)
417 {
418     OSStatus err = noErr;
419
420     err = AUGraphStop(graph);
421     if (err != noErr)
422     {
423         ERR_(midi)("AUGraphStop(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
424         return 0;
425     }
426
427     err = DisposeAUGraph(graph);
428     if (err != noErr)
429     {
430         ERR_(midi)("DisposeAUGraph(%p) return %c%c%c%c\n", graph, (char) (err >> 24), (char) (err >> 16), (char) (err >> 8), (char) err);
431         return 0;
432     }
433
434     return 1;
435 }
436
437 #endif