/* * Wine Driver for CoreAudio / AudioUnit * * Copyright 2005, 2006 Emmanuel Maillard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #define ULONG CoreFoundation_ULONG #define HRESULT CoreFoundation_HRESULT #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H #include #endif #include #include #undef ULONG #undef HRESULT #undef DPRINTF #undef STDMETHODCALLTYPE #include "coreaudio.h" #include "wine/debug.h" #ifndef HAVE_AUDIOUNIT_AUDIOCOMPONENT_H /* Define new AudioComponent Manager functions for compatibility's sake */ typedef Component AudioComponent; typedef ComponentDescription AudioComponentDescription; typedef ComponentInstance AudioComponentInstance; static inline AudioComponent AudioComponentFindNext(AudioComponent ac, AudioComponentDescription *desc) { return FindNextComponent(ac, desc); } static inline OSStatus AudioComponentInstanceNew(AudioComponent ac, AudioComponentInstance *aci) { return OpenAComponent(ac, aci); } static inline OSStatus AudioComponentInstanceDispose(AudioComponentInstance aci) { return CloseComponent(aci); } #endif #ifndef HAVE_AUGRAPHADDNODE static inline OSStatus AUGraphAddNode(AUGraph graph, const AudioComponentDescription *desc, AUNode *node) { return AUGraphNewNode(graph, desc, 0, NULL, node); } static inline OSStatus AUGraphNodeInfo(AUGraph graph, AUNode node, AudioComponentDescription *desc, AudioUnit *au) { return AUGraphGetNodeInfo(graph, node, desc, 0, NULL, au); } #endif WINE_DEFAULT_DEBUG_CHANNEL(wave); WINE_DECLARE_DEBUG_CHANNEL(midi); static const char *streamDescription(const AudioStreamBasicDescription* stream) { 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", stream->mSampleRate, wine_dbgstr_fourcc(stream->mFormatID), stream->mFormatFlags, stream->mBytesPerPacket, stream->mFramesPerPacket, stream->mBytesPerFrame, stream->mChannelsPerFrame, stream->mBitsPerChannel); } int AudioUnit_CloseAudioUnit(AudioUnit au) { OSStatus err = AudioComponentInstanceDispose(au); return (err == noErr); } int AudioUnit_InitializeWithStreamDescription(AudioUnit au, AudioStreamBasicDescription *stream) { OSStatus err = noErr; TRACE("input format: %s\n", streamDescription(stream)); err = AudioUnitSetProperty(au, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, stream, sizeof(*stream)); if (err != noErr) { ERR("AudioUnitSetProperty return an error %s\n", wine_dbgstr_fourcc(err)); return 0; } err = AudioUnitInitialize(au); if (err != noErr) { ERR("AudioUnitInitialize return an error %s\n", wine_dbgstr_fourcc(err)); return 0; } return 1; } int AudioUnit_SetVolume(AudioUnit au, float left, float right) { OSStatus err = noErr; static int once; if (!once++) FIXME("independent left/right volume not implemented (%f, %f)\n", left, right); err = AudioUnitSetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left, 0); if (err != noErr) { ERR("AudioUnitSetParameter return an error %s\n", wine_dbgstr_fourcc(err)); return 0; } return 1; } int AudioUnit_GetVolume(AudioUnit au, float *left, float *right) { OSStatus err = noErr; static int once; if (!once++) FIXME("independent left/right volume not implemented\n"); err = AudioUnitGetParameter(au, kHALOutputParam_Volume, kAudioUnitParameterFlag_Output, 0, left); if (err != noErr) { ERR("AudioUnitGetParameter return an error %s\n", wine_dbgstr_fourcc(err)); return 0; } *right = *left; return 1; } /* FIXME: implement sample rate conversion on input */ int AudioUnit_GetInputDeviceSampleRate(void) { AudioDeviceID defaultInputDevice; UInt32 param; AudioObjectPropertyAddress propertyAddress; Float64 sampleRate; OSStatus err; param = sizeof(defaultInputDevice); propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; propertyAddress.mScope = kAudioObjectPropertyScopeGlobal; propertyAddress.mElement = kAudioObjectPropertyElementMaster; err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, ¶m, &defaultInputDevice); if (err != noErr || defaultInputDevice == kAudioDeviceUnknown) { ERR("Couldn't get the default audio input device ID: %08lx\n", err); return 0; } param = sizeof(sampleRate); propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; propertyAddress.mScope = kAudioDevicePropertyScopeInput; err = AudioObjectGetPropertyData(defaultInputDevice, &propertyAddress, 0, NULL, ¶m, &sampleRate); if (err != noErr) { ERR("Couldn't get the device sample rate: %08lx\n", err); return 0; } return sampleRate; } /* * MIDI Synth Unit */ int SynthUnit_CreateDefaultSynthUnit(AUGraph *graph, AudioUnit *synth) { OSStatus err; AudioComponentDescription desc; AUNode synthNode; AUNode outNode; err = NewAUGraph(graph); if (err != noErr) { ERR_(midi)("NewAUGraph return %s\n", wine_dbgstr_fourcc(err)); return 0; } desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; /* create synth node */ desc.componentType = kAudioUnitType_MusicDevice; desc.componentSubType = kAudioUnitSubType_DLSSynth; err = AUGraphAddNode(*graph, &desc, &synthNode); if (err != noErr) { ERR_(midi)("AUGraphAddNode cannot create synthNode : %s\n", wine_dbgstr_fourcc(err)); return 0; } /* create out node */ desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; err = AUGraphAddNode(*graph, &desc, &outNode); if (err != noErr) { ERR_(midi)("AUGraphAddNode cannot create outNode %s\n", wine_dbgstr_fourcc(err)); return 0; } err = AUGraphOpen(*graph); if (err != noErr) { ERR_(midi)("AUGraphOpen return %s\n", wine_dbgstr_fourcc(err)); return 0; } /* connecting the nodes */ err = AUGraphConnectNodeInput(*graph, synthNode, 0, outNode, 0); if (err != noErr) { ERR_(midi)("AUGraphConnectNodeInput cannot connect synthNode to outNode : %s\n", wine_dbgstr_fourcc(err)); return 0; } /* Get the synth unit */ err = AUGraphNodeInfo(*graph, synthNode, 0, synth); if (err != noErr) { ERR_(midi)("AUGraphNodeInfo return %s\n", wine_dbgstr_fourcc(err)); return 0; } return 1; } int SynthUnit_Initialize(AudioUnit synth, AUGraph graph) { OSStatus err = noErr; err = AUGraphInitialize(graph); if (err != noErr) { ERR_(midi)("AUGraphInitialize(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); return 0; } err = AUGraphStart(graph); if (err != noErr) { ERR_(midi)("AUGraphStart(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); return 0; } return 1; } int SynthUnit_Close(AUGraph graph) { OSStatus err = noErr; err = AUGraphStop(graph); if (err != noErr) { ERR_(midi)("AUGraphStop(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); return 0; } err = DisposeAUGraph(graph); if (err != noErr) { ERR_(midi)("DisposeAUGraph(%p) return %s\n", graph, wine_dbgstr_fourcc(err)); return 0; } return 1; }