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