24 #include <freerdp/config.h>
30 #include <winpr/crt.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;
55 static 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);
69 static 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";
126 static void rdpsnd_mac_release(rdpsndMacPlugin *mac)
129 [mac->player release];
133 [mac->engine release];
137 static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device,
const AUDIO_FORMAT *format, UINT32 latency)
141 AudioDeviceID outputDeviceID;
145 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
146 AudioObjectPropertyAddress propertyAddress = {
147 kAudioHardwarePropertyDefaultOutputDevice,
148 kAudioObjectPropertyScopeGlobal,
149 #if defined(MAC_OS_VERSION_12_0)
150 kAudioObjectPropertyElementMain
152 kAudioObjectPropertyElementMaster
159 if (!rdpsnd_mac_set_format(device, format, latency))
162 propertySize =
sizeof(outputDeviceID);
163 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL,
164 &propertySize, &outputDeviceID);
167 WLog_ERR(TAG,
"AudioHardwareGetProperty: %s", FormatError(err));
171 mac->engine = [[AVAudioEngine alloc] init];
175 err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit,
176 kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
177 0, &outputDeviceID,
sizeof(outputDeviceID));
180 rdpsnd_mac_release(mac);
181 WLog_ERR(TAG,
"AudioUnitSetProperty: %s", FormatError(err));
185 mac->player = [[AVAudioPlayerNode alloc] init];
188 rdpsnd_mac_release(mac);
189 WLog_ERR(TAG,
"AVAudioPlayerNode::init() failed");
193 [mac->engine attachNode:mac->player];
195 [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil];
197 [mac->engine prepare];
199 if (![mac->engine startAndReturnError:&error])
201 device->Close(device);
202 WLog_ERR(TAG,
"Failed to start audio player %s",
203 [error.localizedDescription UTF8String]);
212 static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
216 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
221 mac->isPlaying = FALSE;
230 rdpsnd_mac_release(mac);
234 static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
236 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
237 device->Close(device);
241 static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device,
const AUDIO_FORMAT *format)
243 WINPR_UNUSED(device);
245 switch (format->wFormatTag)
247 case WAVE_FORMAT_PCM:
248 if (format->wBitsPerSample != 16)
251 if (format->nChannels != 2)
260 static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value)
267 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
272 volumeLeft = (value & 0xFFFF);
273 volumeRight = ((value >> 16) & 0xFFFF);
274 fVolume = ((float)volumeLeft) / 65535.0f;
276 mac->player.volume = fVolume;
282 static void rdpsnd_mac_start(rdpsndDevicePlugin *device)
286 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
290 if (!mac->engine.isRunning)
294 if (![mac->engine startAndReturnError:&error])
296 device->Close(device);
297 WLog_ERR(TAG,
"Failed to start audio player %s",
298 [error.localizedDescription UTF8String]);
305 mac->isPlaying = TRUE;
311 static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device,
const BYTE *data,
size_t size)
315 rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
316 AVAudioPCMBuffer *buffer;
317 AVAudioFormat *format;
320 AVAudioFrameCount count;
321 UINT64 start = GetTickCount64();
326 step = 2 * mac->format.nChannels;
329 format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
330 sampleRate:mac->format.nSamplesPerSec
331 channels:mac->format.nChannels
336 WLog_WARN(TAG,
"AVAudioFormat::init() failed");
340 buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count];
345 WLog_WARN(TAG,
"AVAudioPCMBuffer::init() failed");
349 buffer.frameLength = buffer.frameCapacity;
350 db = buffer.floatChannelData;
352 for (
size_t pos = 0; pos < count; pos++)
354 const BYTE *d = &data[pos * step];
355 for (
size_t x = 0; x < mac->format.nChannels; x++)
357 const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f;
359 d += sizeof(int16_t);
363 rdpsnd_mac_start(device);
365 [mac->player scheduleBuffer:buffer
367 UINT64 stop = GetTickCount64();
371 mac->diff = stop - start;
376 return mac->diff > UINT_MAX ? UINT_MAX : mac->diff;
385 FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_rdpsnd_client_subsystem_entry(
388 rdpsndMacPlugin *mac;
389 mac = (rdpsndMacPlugin *)calloc(1,
sizeof(rdpsndMacPlugin));
392 return CHANNEL_RC_NO_MEMORY;
394 mac->device.Open = rdpsnd_mac_open;
395 mac->device.FormatSupported = rdpsnd_mac_format_supported;
396 mac->device.SetVolume = rdpsnd_mac_set_volume;
397 mac->device.Play = rdpsnd_mac_play;
398 mac->device.Close = rdpsnd_mac_close;
399 mac->device.Free = rdpsnd_mac_free;
400 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin *)mac);
401 return CHANNEL_RC_OK;