24#include <freerdp/config.h>
31#include <winpr/sysinfo.h>
33#include <freerdp/types.h>
35#include <AVFoundation/AVAudioBuffer.h>
36#include <AVFoundation/AVFoundation.h>
38#include "rdpsnd_main.h"
42 rdpsndDevicePlugin device;
50 AVAudioEngine *engine;
51 AVAudioPlayerNode *player;
55static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin *device,
const AUDIO_FORMAT *format,
58 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
62 mac->latency = latency;
63 mac->format = *format;
65 audio_format_print(WLog_Get(TAG), WLOG_DEBUG, format);
69static char *FormatError(OSStatus st)
73 case kAudioFileUnspecifiedError:
74 return "kAudioFileUnspecifiedError";
76 case kAudioFileUnsupportedFileTypeError:
77 return "kAudioFileUnsupportedFileTypeError";
79 case kAudioFileUnsupportedDataFormatError:
80 return "kAudioFileUnsupportedDataFormatError";
82 case kAudioFileUnsupportedPropertyError:
83 return "kAudioFileUnsupportedPropertyError";
85 case kAudioFileBadPropertySizeError:
86 return "kAudioFileBadPropertySizeError";
88 case kAudioFilePermissionsError:
89 return "kAudioFilePermissionsError";
91 case kAudioFileNotOptimizedError:
92 return "kAudioFileNotOptimizedError";
94 case kAudioFileInvalidChunkError:
95 return "kAudioFileInvalidChunkError";
97 case kAudioFileDoesNotAllow64BitDataSizeError:
98 return "kAudioFileDoesNotAllow64BitDataSizeError";
100 case kAudioFileInvalidPacketOffsetError:
101 return "kAudioFileInvalidPacketOffsetError";
103 case kAudioFileInvalidFileError:
104 return "kAudioFileInvalidFileError";
106 case kAudioFileOperationNotSupportedError:
107 return "kAudioFileOperationNotSupportedError";
109 case kAudioFileNotOpenError:
110 return "kAudioFileNotOpenError";
112 case kAudioFileEndOfFileError:
113 return "kAudioFileEndOfFileError";
115 case kAudioFilePositionError:
116 return "kAudioFilePositionError";
118 case kAudioFileFileNotFoundError:
119 return "kAudioFileFileNotFoundError";
122 return "unknown error";
126static void rdpsnd_mac_release(rdpsndMacPlugin *mac)
129 [mac->player release];
130 mac->player =
nullptr;
133 [mac->engine release];
134 mac->engine =
nullptr;
137static BOOL rdpsnd_mac_connect_player(rdpsndMacPlugin *mac)
141 AVAudioFormat *format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
142 sampleRate:mac->format.nSamplesPerSec
143 channels:mac->format.nChannels
147 WLog_ERR(TAG,
"AVAudioFormat::init() failed");
151 [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:format];
156static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device,
const AUDIO_FORMAT *format, UINT32 latency)
160 AudioDeviceID outputDeviceID;
164 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
165 AudioObjectPropertyAddress propertyAddress = {
166 kAudioHardwarePropertyDefaultOutputDevice,
167 kAudioObjectPropertyScopeGlobal,
168#if defined(MAC_OS_VERSION_12_0)
169 kAudioObjectPropertyElementMain
171 kAudioObjectPropertyElementMaster
178 if (!rdpsnd_mac_set_format(device, format, latency))
181 propertySize =
sizeof(outputDeviceID);
182 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0,
nullptr,
183 &propertySize, &outputDeviceID);
186 WLog_ERR(TAG,
"AudioHardwareGetProperty: %s", FormatError(err));
190 mac->engine = [[AVAudioEngine alloc] init];
194 err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
195 kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
196 0, &outputDeviceID,
sizeof(outputDeviceID));
199 rdpsnd_mac_release(mac);
200 WLog_ERR(TAG,
"AudioUnitSetProperty: %s", FormatError(err));
204 mac->player = [[AVAudioPlayerNode alloc] init];
207 rdpsnd_mac_release(mac);
208 WLog_ERR(TAG,
"AVAudioPlayerNode::init() failed");
212 [mac->engine attachNode:mac->player];
214 if (!rdpsnd_mac_connect_player(mac))
216 rdpsnd_mac_release(mac);
220 [mac->engine prepare];
222 if (![mac->engine startAndReturnError:&error])
224 device->Close(device);
225 WLog_ERR(TAG,
"Failed to start audio player %s",
226 [error.localizedDescription UTF8String]);
235static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
239 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
244 mac->isPlaying = FALSE;
253 rdpsnd_mac_release(mac);
257static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
259 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
260 device->Close(device);
264static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device,
const AUDIO_FORMAT *format)
266 WINPR_UNUSED(device);
268 switch (format->wFormatTag)
270 case WAVE_FORMAT_PCM:
271 if (format->wBitsPerSample != 16)
274 if (format->nChannels != 2)
283static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
290 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
295 volumeLeft = (value & 0xFFFF);
296 volumeRight = ((value >> 16) & 0xFFFF);
297 fVolume = ((float)volumeLeft) / 65535.0f;
299 mac->player.volume = fVolume;
305static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
309 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
311 if (!mac->engine.isRunning)
314 if (!rdpsnd_mac_connect_player(mac))
316 device->Close(device);
319 [mac->engine prepare];
320 if (![mac->engine startAndReturnError:&error])
322 device->Close(device);
323 WLog_ERR(TAG,
"Failed to start audio player %s",
324 [error.localizedDescription UTF8String]);
327 mac->isPlaying = FALSE;
334 mac->isPlaying = TRUE;
340static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device,
const BYTE *data,
size_t size)
344 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
345 AVAudioPCMBuffer *buffer;
346 AVAudioFormat *format;
349 AVAudioFrameCount count;
350 UINT64 start = GetTickCount64();
355 step = 2 * mac->format.nChannels;
358 format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
359 sampleRate:mac->format.nSamplesPerSec
360 channels:mac->format.nChannels
365 WLog_WARN(TAG,
"AVAudioFormat::init() failed");
369 buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
374 WLog_WARN(TAG,
"AVAudioPCMBuffer::init() failed");
378 buffer.frameLength = buffer.frameCapacity;
379 db = buffer.floatChannelData;
381 for (
size_t pos = 0; pos < count; pos++)
383 const BYTE *d = &data[pos * step];
384 for (
size_t x = 0; x < mac->format.nChannels; x++)
386 const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
388 d += sizeof(int16_t);
392 rdpsnd_mac_start(device);
394 [mac->player scheduleBuffer:buffer
396 UINT64 stop = GetTickCount64();
400 mac->diff = stop - start;
405 return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
414FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_rdpsnd_client_subsystem_entry(
417 rdpsndMacPlugin *mac;
418 mac = (rdpsndMacPlugin *)calloc(1,
sizeof(rdpsndMacPlugin));
421 return CHANNEL_RC_NO_MEMORY;
423 mac->device.Open = rdpsnd_mac_open;
424 mac->device.FormatSupported = rdpsnd_mac_format_supported;
425 mac->device.SetVolume = rdpsnd_mac_set_volume;
426 mac->device.Play = rdpsnd_mac_play;
427 mac->device.Close = rdpsnd_mac_close;
428 mac->device.Free = rdpsnd_mac_free;
429 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin *)mac);
430 return CHANNEL_RC_OK;