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];
 
  133    [mac->engine release];
 
  137static 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]);
 
  212static void rdpsnd_mac_close(rdpsndDevicePlugin *device)
 
  216    rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
 
  221      mac->isPlaying = FALSE;
 
  230    rdpsnd_mac_release(mac);
 
  234static void rdpsnd_mac_free(rdpsndDevicePlugin *device)
 
  236  rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device;
 
  237  device->Close(device);
 
  241static 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)
 
  260static 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;
 
  282static 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;
 
  311static 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;
 
  385FREERDP_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;