2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD 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.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file cocoa_s.cpp Sound driver for cocoa. */
10 /*****************************************************************************
11 * Cocoa sound driver *
12 * Known things left to do: *
13 * - Might need to do endian checking for it to work on both ppc and x86 *
14 *****************************************************************************/
18 #include "../stdafx.h"
19 #include "../os/macosx/macos.h"
21 #include "../driver.h"
26 #define Point OTTDPoint
27 #include <AudioUnit/AudioUnit.h>
31 #include "../safeguards.h"
33 static FSoundDriver_Cocoa iFSoundDriver_Cocoa
;
35 static AudioUnit _outputAudioUnit
;
37 /* The CoreAudio callback */
38 static OSStatus
audioCallback(void *, AudioUnitRenderActionFlags
*, const AudioTimeStamp
*, UInt32
, UInt32
, AudioBufferList
*ioData
)
40 MxMixSamples(ioData
->mBuffers
[0].mData
, ioData
->mBuffers
[0].mDataByteSize
/ 4);
46 std::optional
<std::string_view
> SoundDriver_Cocoa::Start(const StringList
&parm
)
48 struct AURenderCallbackStruct callback
;
49 AudioStreamBasicDescription requestedDesc
;
51 /* Setup a AudioStreamBasicDescription with the requested format */
52 requestedDesc
.mFormatID
= kAudioFormatLinearPCM
;
53 requestedDesc
.mFormatFlags
= kLinearPCMFormatFlagIsPacked
;
54 requestedDesc
.mChannelsPerFrame
= 2;
55 requestedDesc
.mSampleRate
= GetDriverParamInt(parm
, "hz", 44100);
57 requestedDesc
.mBitsPerChannel
= 16;
58 requestedDesc
.mFormatFlags
|= kLinearPCMFormatFlagIsSignedInteger
;
60 if constexpr (std::endian::native
== std::endian::big
) {
61 requestedDesc
.mFormatFlags
|= kLinearPCMFormatFlagIsBigEndian
;
64 requestedDesc
.mFramesPerPacket
= 1;
65 requestedDesc
.mBytesPerFrame
= requestedDesc
.mBitsPerChannel
* requestedDesc
.mChannelsPerFrame
/ 8;
66 requestedDesc
.mBytesPerPacket
= requestedDesc
.mBytesPerFrame
* requestedDesc
.mFramesPerPacket
;
68 MxInitialize((uint
)requestedDesc
.mSampleRate
);
70 /* Locate the default output audio unit */
71 AudioComponentDescription desc
;
72 desc
.componentType
= kAudioUnitType_Output
;
73 desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
74 desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
75 desc
.componentFlags
= 0;
76 desc
.componentFlagsMask
= 0;
78 AudioComponent comp
= AudioComponentFindNext (nullptr, &desc
);
79 if (comp
== nullptr) {
80 return "cocoa_s: Failed to start CoreAudio: AudioComponentFindNext returned nullptr";
83 /* Open & initialize the default output audio unit */
84 if (AudioComponentInstanceNew(comp
, &_outputAudioUnit
) != noErr
) {
85 return "cocoa_s: Failed to start CoreAudio: AudioComponentInstanceNew";
88 if (AudioUnitInitialize(_outputAudioUnit
) != noErr
) {
89 return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
92 /* Set the input format of the audio unit. */
93 if (AudioUnitSetProperty(_outputAudioUnit
, kAudioUnitProperty_StreamFormat
, kAudioUnitScope_Input
, 0, &requestedDesc
, sizeof(requestedDesc
)) != noErr
) {
94 return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)";
97 /* Set the audio callback */
98 callback
.inputProc
= audioCallback
;
99 callback
.inputProcRefCon
= nullptr;
100 if (AudioUnitSetProperty(_outputAudioUnit
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &callback
, sizeof(callback
)) != noErr
) {
101 return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback)";
104 /* Finally, start processing of the audio unit */
105 if (AudioOutputUnitStart(_outputAudioUnit
) != noErr
) {
106 return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
114 void SoundDriver_Cocoa::Stop()
116 struct AURenderCallbackStruct callback
;
118 /* stop processing the audio unit */
119 if (AudioOutputUnitStop(_outputAudioUnit
) != noErr
) {
120 Debug(driver
, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
124 /* Remove the input callback */
125 callback
.inputProc
= 0;
126 callback
.inputProcRefCon
= 0;
127 if (AudioUnitSetProperty(_outputAudioUnit
, kAudioUnitProperty_SetRenderCallback
, kAudioUnitScope_Input
, 0, &callback
, sizeof(callback
)) != noErr
) {
128 Debug(driver
, 0, "cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetRenderCallback) failed");
132 if (AudioComponentInstanceDispose(_outputAudioUnit
) != noErr
) {
133 Debug(driver
, 0, "cocoa_s: Core_CloseAudio: AudioComponentInstanceDispose failed");
138 #endif /* WITH_COCOA */