OpenTTD Source 20241224-master-gee860a5c8e
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
33static FSoundDriver_Cocoa iFSoundDriver_Cocoa;
34
35static AudioUnit _outputAudioUnit;
36
37/* The CoreAudio callback */
38static 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
46std::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 */
void Stop() override
Stop this driver.
std::optional< std::string_view > Start(const StringList &param) override
Start this driver.
Base for Cocoa sound handling.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
int GetDriverParamInt(const StringList &parm, const char *name, int def)
Get an integer parameter the list of parameters.
Definition driver.cpp:76
std::vector< std::string > StringList
Type for a list of strings.
Definition string_type.h:60