winecoreaudio: Quiet a noisy FIXME.
[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 "coreaudio.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(wave);
39 WINE_DECLARE_DEBUG_CHANNEL(midi);
40
41 static const char *streamDescription(const AudioStreamBasicDescription* stream)
42 {
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);
52 }
53
54 extern OSStatus CoreAudio_woAudioUnitIOProc(void *inRefCon, 
55                                 AudioUnitRenderActionFlags *ioActionFlags, 
56                                 const AudioTimeStamp *inTimeStamp, 
57                                 UInt32 inBusNumber, 
58                                 UInt32 inNumberFrames, 
59                                 AudioBufferList *ioData);
60
61 extern OSStatus CoreAudio_wiAudioUnitIOProc(void *inRefCon,
62                                 AudioUnitRenderActionFlags *ioActionFlags,
63                                 const AudioTimeStamp *inTimeStamp,
64                                 UInt32 inBusNumber,
65                                 UInt32 inNumberFrames,
66                                 AudioBufferList *ioData);
67
68 int AudioUnit_CreateDefaultAudioUnit(void *wwo, AudioUnit *au)
69 {
70     OSStatus err;
71     Component comp;
72     ComponentDescription desc;
73     AURenderCallbackStruct callbackStruct;
74
75     TRACE("\n");
76     
77     desc.componentType = kAudioUnitType_Output;
78     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
79     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
80     desc.componentFlags = 0;
81     desc.componentFlagsMask = 0;
82
83     comp = FindNextComponent(NULL, &desc);
84     if (comp == NULL)
85         return 0;
86     
87     err = OpenAComponent(comp, au);
88     if (err != noErr || *au == NULL)
89         return 0;
90         
91     callbackStruct.inputProc = CoreAudio_woAudioUnitIOProc;
92     callbackStruct.inputProcRefCon = wwo;
93
94     err = AudioUnitSetProperty( *au, 
95                                 kAudioUnitProperty_SetRenderCallback, 
96                                 kAudioUnitScope_Input,
97                                 0, 
98                                 &callbackStruct, 
99                                 sizeof(callbackStruct));
100     return (err == noErr);
101 }
102
103 int AudioUnit_CloseAudioUnit(AudioUnit au)
104 {
105     OSStatus err = CloseComponent(au);
106     return (err == noErr);
107 }
108
109 int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *stream)
110 {
111     OSStatus err = noErr;
112         
113     TRACE("input format: %s\n", streamDescription(stream));
114
115     err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
116                                 0, stream, sizeof(*stream));
117
118     if (err != noErr)
119     {
120         ERR("AudioUnitSetProperty return an error %s\n", wine_dbgstr_fourcc(err));
121         return 0;
122     }
123     
124     err = AudioUnitInitialize(au);
125     if (err != noErr)
126     {
127         ERR("AudioUnitInitialize return an error %s\n", wine_dbgstr_fourcc(err));
128         return 0;
129     }
130     return 1;
131 }
132
133 int AudioUnit_SetVolume(AudioUnit au, float left, float right)
134 {
135     OSStatus err = noErr;
136     static int once;
137
138     if (!once++) FIXME("independent left/right volume not implemented (%f, %f)\n", left, right);
139    
140     err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0);
141                                 
142     if (err != noErr)
143     {
144         ERR("AudioUnitSetParameter return an error %s\n", wine_dbgstr_fourcc(err));
145         return 0;
146     }
147     return 1;
148 }
149
150 int AudioUnit_GetVolume(AudioUnit au, float *left, float *right)
151 {
152     OSStatus err = noErr;
153     static int once;
154
155     if (!once++) FIXME("independent left/right volume not implemented\n");
156     
157     err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left);
158     if (err != noErr)
159     {
160         ERR("AudioUnitGetParameter return an error %s\n", wine_dbgstr_fourcc(err));
161         return 0;
162     }
163     *right = *left;
164     return 1;
165 }
166
167
168 /* FIXME: implement sample rate conversion on input */
169 int AudioUnit_GetInputDeviceSampleRate(void)
170 {
171     AudioDeviceID               defaultInputDevice;
172     UInt32                      param;
173     Float64                     sampleRate;
174     OSStatus                    err;
175
176     param = sizeof(defaultInputDevice);
177     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
178     if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
179     {
180         ERR("Couldn't get the default audio input device ID: %08lx\n", err);
181         return 0;
182     }
183
184     param = sizeof(sampleRate);
185     err = AudioDeviceGetProperty(defaultInputDevice, 0, 1, kAudioDevicePropertyNominalSampleRate, &param, &sampleRate);
186     if (err != noErr)
187     {
188         ERR("Couldn't get the device sample rate: %08lx\n", err);
189         return 0;
190     }
191
192     return sampleRate;
193 }
194
195
196 int AudioUnit_CreateInputUnit(void* wwi, AudioUnit* out_au,
197         WORD nChannels, DWORD nSamplesPerSec, WORD wBitsPerSample,
198         UInt32* outFrameCount)
199 {
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;
208
209
210     if (!outFrameCount)
211     {
212         ERR("Invalid parameter\n");
213         return 0;
214     }
215
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;
222
223     component = FindNextComponent(NULL, &description);
224     if (!component)
225     {
226         ERR("FindNextComponent(kAudioUnitSubType_HALOutput) failed\n");
227         return 0;
228     }
229
230     err = OpenAComponent(component, &au);
231     if (err != noErr || au == NULL)
232     {
233         ERR("OpenAComponent failed: %08lx\n", err);
234         return 0;
235     }
236
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:
243      *
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
248      */
249
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)
254     {
255         ERR("Couldn't enable input on AUHAL: %08lx\n", err);
256         goto error;
257     }
258
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)
263     {
264         ERR("Couldn't disable output on AUHAL: %08lx\n", err);
265         goto error;
266     }
267
268     /* Find the default input device */
269     param = sizeof(defaultInputDevice);
270     err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &param, &defaultInputDevice);
271     if (err != noErr || defaultInputDevice == kAudioDeviceUnknown)
272     {
273         ERR("Couldn't get the default audio device ID: %08lx\n", err);
274         goto error;
275     }
276
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)
280     {
281         ERR("Couldn't set current device of AUHAL to default input device: %08lx\n", err);
282         goto error;
283     }
284
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)
292     {
293         ERR("Couldn't set input callback of AUHAL: %08lx\n", err);
294         goto error;
295     }
296
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;
312
313     /* Set the AudioOutputUnit output data format */
314     err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &desiredFormat, sizeof(desiredFormat));
315     if (err != noErr)
316     {
317         ERR("Couldn't set desired input format of AUHAL: %08lx\n", err);
318         goto error;
319     }
320
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)
325     {
326         ERR("Failed to get audio sample size: %08lx\n", err);
327         goto error;
328     }
329
330     TRACE("Frame count: %lu\n", *outFrameCount);
331
332     /* Initialize the AU */
333     err = AudioUnitInitialize(au);
334     if (err != noErr)
335     {
336         ERR("Failed to initialize AU: %08lx\n", err);
337         goto error;
338     }
339
340     *out_au = au;
341
342     return 1;
343
344 error:
345     if (au)
346         CloseComponent(au);
347     return 0;
348 }
349
350 /*
351  *  MIDI Synth Unit
352  */
353 int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth)
354 {
355     OSStatus err;
356     ComponentDescription desc;
357     AUNode synthNode;
358     AUNode outNode;
359
360     err = NewAUGraph(graph);
361     if (err != noErr)
362     {
363         ERR_(midi)("NewAUGraph return %s\n", wine_dbgstr_fourcc(err));
364         return 0;
365     }
366
367     desc.componentManufacturer = kAudioUnitManufacturer_Apple;
368     desc.componentFlags = 0;
369     desc.componentFlagsMask = 0;
370
371     /* create synth node */
372     desc.componentType = kAudioUnitType_MusicDevice;
373     desc.componentSubType = kAudioUnitSubType_DLSSynth;
374
375     err = AUGraphNewNode(*graph, &desc, 0, NULL, &synthNode);
376     if (err != noErr)
377     {
378         ERR_(midi)("AUGraphNewNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(err));
379         return 0;
380     }
381
382     /* create out node */
383     desc.componentType = kAudioUnitType_Output;
384     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
385
386     err = AUGraphNewNode(*graph, &desc, 0, NULL, &outNode);
387     if (err != noErr)
388     {
389         ERR_(midi)("AUGraphNewNode cannot create outNode %s\n", wine_dbgstr_fourcc(err));
390         return 0;
391     }
392
393     err = AUGraphOpen(*graph);
394     if (err != noErr)
395     {
396         ERR_(midi)("AUGraphOpen return %s\n", wine_dbgstr_fourcc(err));
397         return 0;
398     }
399
400     /* connecting the nodes */
401     err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0);
402     if (err != noErr)
403     {
404         ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n", wine_dbgstr_fourcc(err));
405         return 0;
406     }
407
408     /* Get the synth unit */
409     err = AUGraphGetNodeInfo(*graph, synthNode, 0, 0, 0, synth);
410     if (err != noErr)
411     {
412         ERR_(midi)("AUGraphGetNodeInfo return %s\n", wine_dbgstr_fourcc(err));
413         return 0;
414     }
415
416     return 1;
417 }
418
419 int SynthUnit_Initialize(AudioUnit synth, AUGraph graph)
420 {
421     OSStatus err = noErr;
422
423     err = AUGraphInitialize(graph);
424     if (err != noErr)
425     {
426         ERR_(midi)("AUGraphInitialize(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
427         return 0;
428     }
429
430     err = AUGraphStart(graph);
431     if (err != noErr)
432     {
433         ERR_(midi)("AUGraphStart(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
434         return 0;
435     }
436
437     return 1;
438 }
439
440 int SynthUnit_Close(AUGraph graph)
441 {
442     OSStatus err = noErr;
443
444     err = AUGraphStop(graph);
445     if (err != noErr)
446     {
447         ERR_(midi)("AUGraphStop(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
448         return 0;
449     }
450
451     err = DisposeAUGraph(graph);
452     if (err != noErr)
453     {
454         ERR_(midi)("DisposeAUGraph(%p) return %s\n", graph, wine_dbgstr_fourcc(err));
455         return 0;
456     }
457
458     return 1;
459 }
460
461 #endif