OpenTTD Source  20240919-master-gdf0233f4c2
cocoa_s.cpp
Go to the documentation of this file.
1 /*
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/>.
6  */
7 
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  *****************************************************************************/
15 
16 #ifdef WITH_COCOA
17 
18 #include "../stdafx.h"
19 #include "../os/macosx/macos.h"
20 #include "../debug.h"
21 #include "../driver.h"
22 #include "../mixer.h"
23 #include "cocoa_s.h"
24 
25 #define Rect OTTDRect
26 #define Point OTTDPoint
27 #include <AudioUnit/AudioUnit.h>
28 #undef Rect
29 #undef Point
30 
31 #include "../safeguards.h"
32 
33 static FSoundDriver_Cocoa iFSoundDriver_Cocoa;
34 
35 static AudioUnit _outputAudioUnit;
36 
37 /* The CoreAudio callback */
38 static OSStatus audioCallback(void *, AudioUnitRenderActionFlags *, const AudioTimeStamp *, UInt32, UInt32, AudioBufferList *ioData)
39 {
40  MxMixSamples(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize / 4);
41 
42  return noErr;
43 }
44 
45 
46 std::optional<std::string_view> SoundDriver_Cocoa::Start(const StringList &parm)
47 {
48  struct AURenderCallbackStruct callback;
49  AudioStreamBasicDescription requestedDesc;
50 
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);
56 
57  requestedDesc.mBitsPerChannel = 16;
58  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
59 
60  if constexpr (std::endian::native == std::endian::big) {
61  requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
62  }
63 
64  requestedDesc.mFramesPerPacket = 1;
65  requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
66  requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
67 
68  MxInitialize((uint)requestedDesc.mSampleRate);
69 
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;
77 
78  AudioComponent comp = AudioComponentFindNext (nullptr, &desc);
79  if (comp == nullptr) {
80  return "cocoa_s: Failed to start CoreAudio: AudioComponentFindNext returned nullptr";
81  }
82 
83  /* Open & initialize the default output audio unit */
84  if (AudioComponentInstanceNew(comp, &_outputAudioUnit) != noErr) {
85  return "cocoa_s: Failed to start CoreAudio: AudioComponentInstanceNew";
86  }
87 
88  if (AudioUnitInitialize(_outputAudioUnit) != noErr) {
89  return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
90  }
91 
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)";
95  }
96 
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)";
102  }
103 
104  /* Finally, start processing of the audio unit */
105  if (AudioOutputUnitStart(_outputAudioUnit) != noErr) {
106  return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
107  }
108 
109  /* We're running! */
110  return std::nullopt;
111 }
112 
113 
115 {
116  struct AURenderCallbackStruct callback;
117 
118  /* stop processing the audio unit */
119  if (AudioOutputUnitStop(_outputAudioUnit) != noErr) {
120  Debug(driver, 0, "cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
121  return;
122  }
123 
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");
129  return;
130  }
131 
132  if (AudioComponentInstanceDispose(_outputAudioUnit) != noErr) {
133  Debug(driver, 0, "cocoa_s: Core_CloseAudio: AudioComponentInstanceDispose failed");
134  return;
135  }
136 }
137 
138 #endif /* WITH_COCOA */
cocoa_s.h
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
SoundDriver_Cocoa::Stop
void Stop() override
Stop this driver.
StringList
std::vector< std::string > StringList
Type for a list of strings.
Definition: string_type.h:60
SoundDriver_Cocoa::Start
std::optional< std::string_view > Start(const StringList &param) override
Start this driver.
FSoundDriver_Cocoa
Definition: cocoa_s.h:23
GetDriverParamInt
int GetDriverParamInt(const StringList &parm, const char *name, int def)
Get an integer parameter the list of parameters.
Definition: driver.cpp:76