Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 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 |
Alexandre Julliard | ea50196 | 2006-06-01 13:15:54 +0200 | [diff] [blame] | 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 19 | */ |
| 20 | |
| 21 | #include "config.h" |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 22 | |
Ken Thomases | 131a046 | 2009-05-01 23:01:18 -0500 | [diff] [blame] | 23 | #define ULONG CoreFoundation_ULONG |
| 24 | #define HRESULT CoreFoundation_HRESULT |
Charles Davis | 459d942 | 2011-01-29 16:45:53 -0700 | [diff] [blame] | 25 | #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H |
Ken Thomases | 131a046 | 2009-05-01 23:01:18 -0500 | [diff] [blame] | 26 | #include <CoreServices/CoreServices.h> |
Charles Davis | 459d942 | 2011-01-29 16:45:53 -0700 | [diff] [blame] | 27 | #endif |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 28 | #include <AudioUnit/AudioUnit.h> |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 29 | #include <AudioToolbox/AudioToolbox.h> |
Ken Thomases | 131a046 | 2009-05-01 23:01:18 -0500 | [diff] [blame] | 30 | #undef ULONG |
| 31 | #undef HRESULT |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 32 | |
Alexandre Julliard | ab1040b | 2009-01-02 13:54:49 +0100 | [diff] [blame] | 33 | #undef DPRINTF |
Ken Thomases | 131a046 | 2009-05-01 23:01:18 -0500 | [diff] [blame] | 34 | #undef STDMETHODCALLTYPE |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 35 | #include "coreaudio.h" |
Andrew Talbot | 1d5d237 | 2008-12-20 15:02:41 +0000 | [diff] [blame] | 36 | #include "wine/debug.h" |
| 37 | |
Charles Davis | 459d942 | 2011-01-29 16:45:53 -0700 | [diff] [blame] | 38 | #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H |
| 39 | /* Define new AudioComponent Manager functions for compatibility's sake */ |
| 40 | typedef Component AudioComponent; |
| 41 | typedef ComponentDescription AudioComponentDescription; |
| 42 | typedef ComponentInstance AudioComponentInstance; |
| 43 | |
| 44 | static inline AudioComponent AudioComponentFindNext(AudioComponent ac, AudioComponentDescription *desc) |
| 45 | { |
| 46 | return FindNextComponent(ac, desc); |
| 47 | } |
| 48 | |
| 49 | static inline OSStatus AudioComponentInstanceNew(AudioComponent ac, AudioComponentInstance *aci) |
| 50 | { |
| 51 | return OpenAComponent(ac, aci); |
| 52 | } |
| 53 | |
| 54 | static inline OSStatus AudioComponentInstanceDispose(AudioComponentInstance aci) |
| 55 | { |
| 56 | return CloseComponent(aci); |
| 57 | } |
| 58 | #endif |
| 59 | |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 60 | #ifndef HAVE_AUGRAPHADDNODE |
| 61 | static inline OSStatus AUGraphAddNode(AUGraph graph, const AudioComponentDescription *desc, AUNode *node) |
| 62 | { |
| 63 | return AUGraphNewNode(graph, desc, 0, NULL, node); |
| 64 | } |
| 65 | |
| 66 | static inline OSStatus AUGraphNodeInfo(AUGraph graph, AUNode node, AudioComponentDescription *desc, AudioUnit *au) |
| 67 | { |
| 68 | return AUGraphGetNodeInfo(graph, node, desc, 0, NULL, au); |
| 69 | } |
| 70 | #endif |
| 71 | |
Andrew Talbot | 1d5d237 | 2008-12-20 15:02:41 +0000 | [diff] [blame] | 72 | WINE_DEFAULT_DEBUG_CHANNEL(wave); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 73 | WINE_DECLARE_DEBUG_CHANNEL(midi); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 74 | |
Ken Thomases | e690d7c | 2009-10-11 14:11:37 -0500 | [diff] [blame] | 75 | static const char *streamDescription(const AudioStreamBasicDescription* stream) |
| 76 | { |
| 77 | 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", |
| 78 | stream->mSampleRate, |
| 79 | wine_dbgstr_fourcc(stream->mFormatID), |
| 80 | stream->mFormatFlags, |
| 81 | stream->mBytesPerPacket, |
| 82 | stream->mFramesPerPacket, |
| 83 | stream->mBytesPerFrame, |
| 84 | stream->mChannelsPerFrame, |
| 85 | stream->mBitsPerChannel); |
| 86 | } |
| 87 | |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 88 | int AudioUnit_CloseAudioUnit(AudioUnit au) |
| 89 | { |
Charles Davis | 459d942 | 2011-01-29 16:45:53 -0700 | [diff] [blame] | 90 | OSStatus err = AudioComponentInstanceDispose(au); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 91 | return (err == noErr); |
| 92 | } |
| 93 | |
Alexandre Julliard | 11fe657 | 2006-05-31 14:52:58 +0200 | [diff] [blame] | 94 | int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *stream) |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 95 | { |
| 96 | OSStatus err = noErr; |
| 97 | |
Ken Thomases | e690d7c | 2009-10-11 14:11:37 -0500 | [diff] [blame] | 98 | TRACE("input format: %s\n", streamDescription(stream)); |
| 99 | |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 100 | err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, |
Alexandre Julliard | 11fe657 | 2006-05-31 14:52:58 +0200 | [diff] [blame] | 101 | 0, stream, sizeof(*stream)); |
| 102 | |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 103 | if (err != noErr) |
| 104 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 105 | ERR("AudioUnitSetProperty return an error %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 106 | return 0; |
| 107 | } |
| 108 | |
| 109 | err = AudioUnitInitialize(au); |
| 110 | if (err != noErr) |
| 111 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 112 | ERR("AudioUnitInitialize return an error %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 113 | return 0; |
| 114 | } |
| 115 | return 1; |
| 116 | } |
| 117 | |
| 118 | int AudioUnit_SetVolume(AudioUnit au, float left, float right) |
| 119 | { |
| 120 | OSStatus err = noErr; |
Ken Thomases | f639c4d | 2009-10-15 19:19:20 -0500 | [diff] [blame] | 121 | static int once; |
| 122 | |
| 123 | if (!once++) FIXME("independent left/right volume not implemented (%f, %f)\n", left, right); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 124 | |
| 125 | err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0); |
| 126 | |
| 127 | if (err != noErr) |
| 128 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 129 | ERR("AudioUnitSetParameter return an error %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 130 | return 0; |
| 131 | } |
| 132 | return 1; |
| 133 | } |
| 134 | |
| 135 | int AudioUnit_GetVolume(AudioUnit au, float *left, float *right) |
| 136 | { |
| 137 | OSStatus err = noErr; |
Ken Thomases | f639c4d | 2009-10-15 19:19:20 -0500 | [diff] [blame] | 138 | static int once; |
| 139 | |
| 140 | if (!once++) FIXME("independent left/right volume not implemented\n"); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 141 | |
| 142 | err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left); |
| 143 | if (err != noErr) |
| 144 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 145 | ERR("AudioUnitGetParameter return an error %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 144a535 | 2006-05-28 22:46:23 +0200 | [diff] [blame] | 146 | return 0; |
| 147 | } |
| 148 | *right = *left; |
| 149 | return 1; |
| 150 | } |
Ken Thomases | 30a1b29 | 2006-12-28 11:05:24 -0600 | [diff] [blame] | 151 | |
| 152 | |
Ken Thomases | 0e52c42 | 2006-12-28 11:07:24 -0600 | [diff] [blame] | 153 | /* FIXME: implement sample rate conversion on input */ |
| 154 | int AudioUnit_GetInputDeviceSampleRate(void) |
| 155 | { |
| 156 | AudioDeviceID defaultInputDevice; |
| 157 | UInt32 param; |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 158 | AudioObjectPropertyAddress propertyAddress; |
Ken Thomases | 0e52c42 | 2006-12-28 11:07:24 -0600 | [diff] [blame] | 159 | Float64 sampleRate; |
| 160 | OSStatus err; |
| 161 | |
| 162 | param = sizeof(defaultInputDevice); |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 163 | propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; |
| 164 | propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; |
| 165 | propertyAddress.mElement = kAudioObjectPropertyElementMaster; |
| 166 | err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, ¶m, &defaultInputDevice); |
Ken Thomases | 0e52c42 | 2006-12-28 11:07:24 -0600 | [diff] [blame] | 167 | if (err != noErr || defaultInputDevice == kAudioDeviceUnknown) |
| 168 | { |
| 169 | ERR("Couldn't get the default audio input device ID: %08lx\n", err); |
| 170 | return 0; |
| 171 | } |
| 172 | |
| 173 | param = sizeof(sampleRate); |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 174 | propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; |
| 175 | propertyAddress.mScope = kAudioDevicePropertyScopeInput; |
| 176 | err = AudioObjectGetPropertyData(defaultInputDevice, &propertyAddress, 0, NULL, ¶m, &sampleRate); |
Ken Thomases | 0e52c42 | 2006-12-28 11:07:24 -0600 | [diff] [blame] | 177 | if (err != noErr) |
| 178 | { |
| 179 | ERR("Couldn't get the device sample rate: %08lx\n", err); |
| 180 | return 0; |
| 181 | } |
| 182 | |
| 183 | return sampleRate; |
| 184 | } |
| 185 | |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 186 | /* |
| 187 | * MIDI Synth Unit |
| 188 | */ |
| 189 | int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth) |
| 190 | { |
| 191 | OSStatus err; |
Charles Davis | 459d942 | 2011-01-29 16:45:53 -0700 | [diff] [blame] | 192 | AudioComponentDescription desc; |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 193 | AUNode synthNode; |
| 194 | AUNode outNode; |
| 195 | |
| 196 | err = NewAUGraph(graph); |
| 197 | if (err != noErr) |
| 198 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 199 | ERR_(midi)("NewAUGraph return %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 200 | return 0; |
| 201 | } |
| 202 | |
| 203 | desc.componentManufacturer = kAudioUnitManufacturer_Apple; |
| 204 | desc.componentFlags = 0; |
| 205 | desc.componentFlagsMask = 0; |
| 206 | |
| 207 | /* create synth node */ |
| 208 | desc.componentType = kAudioUnitType_MusicDevice; |
| 209 | desc.componentSubType = kAudioUnitSubType_DLSSynth; |
| 210 | |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 211 | err = AUGraphAddNode(*graph, &desc, &synthNode); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 212 | if (err != noErr) |
| 213 | { |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 214 | ERR_(midi)("AUGraphAddNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | /* create out node */ |
| 219 | desc.componentType = kAudioUnitType_Output; |
| 220 | desc.componentSubType = kAudioUnitSubType_DefaultOutput; |
| 221 | |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 222 | err = AUGraphAddNode(*graph, &desc, &outNode); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 223 | if (err != noErr) |
| 224 | { |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 225 | ERR_(midi)("AUGraphAddNode cannot create outNode %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 226 | return 0; |
| 227 | } |
| 228 | |
| 229 | err = AUGraphOpen(*graph); |
| 230 | if (err != noErr) |
| 231 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 232 | ERR_(midi)("AUGraphOpen return %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 233 | return 0; |
| 234 | } |
| 235 | |
| 236 | /* connecting the nodes */ |
| 237 | err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0); |
| 238 | if (err != noErr) |
| 239 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 240 | ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 241 | return 0; |
| 242 | } |
| 243 | |
| 244 | /* Get the synth unit */ |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 245 | err = AUGraphNodeInfo(*graph, synthNode, 0, synth); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 246 | if (err != noErr) |
| 247 | { |
Charles Davis | b149d7b | 2011-01-29 16:45:54 -0700 | [diff] [blame] | 248 | ERR_(midi)("AUGraphNodeInfo return %s\n", wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | return 1; |
| 253 | } |
| 254 | |
| 255 | int SynthUnit_Initialize(AudioUnit synth, AUGraph graph) |
| 256 | { |
| 257 | OSStatus err = noErr; |
| 258 | |
| 259 | err = AUGraphInitialize(graph); |
| 260 | if (err != noErr) |
| 261 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 262 | ERR_(midi)("AUGraphInitialize(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 263 | return 0; |
| 264 | } |
| 265 | |
| 266 | err = AUGraphStart(graph); |
| 267 | if (err != noErr) |
| 268 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 269 | ERR_(midi)("AUGraphStart(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 270 | return 0; |
| 271 | } |
| 272 | |
| 273 | return 1; |
| 274 | } |
| 275 | |
| 276 | int SynthUnit_Close(AUGraph graph) |
| 277 | { |
| 278 | OSStatus err = noErr; |
| 279 | |
| 280 | err = AUGraphStop(graph); |
| 281 | if (err != noErr) |
| 282 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 283 | ERR_(midi)("AUGraphStop(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 284 | return 0; |
| 285 | } |
| 286 | |
| 287 | err = DisposeAUGraph(graph); |
| 288 | if (err != noErr) |
| 289 | { |
Ken Thomases | 1b5ca01 | 2009-10-11 14:11:19 -0500 | [diff] [blame] | 290 | ERR_(midi)("DisposeAUGraph(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); |
Emmanuel Maillard | 7ad29c8 | 2007-04-25 00:55:33 +0200 | [diff] [blame] | 291 | return 0; |
| 292 | } |
| 293 | |
| 294 | return 1; |
| 295 | } |