22#include <freerdp/config.h> 
   24#include <winpr/assert.h> 
   32#include <winpr/cmdline.h> 
   33#include <winpr/sysinfo.h> 
   34#include <winpr/collections.h> 
   36#include <freerdp/types.h> 
   37#include <freerdp/channels/log.h> 
   40#include "rdpsnd_main.h" 
   44  rdpsndDevicePlugin device;
 
   58} rdpsndopenslesPlugin;
 
   60static int rdpsnd_opensles_volume_to_millibel(
unsigned short level, 
int max)
 
   62  const int min = SL_MILLIBEL_MIN;
 
   63  const int step = max - min;
 
   64  const int rc = (level * step / 0xFFFF) + min;
 
   65  DEBUG_SND(
"level=%hu, min=%d, max=%d, step=%d, result=%d", level, min, max, step, rc);
 
   69static unsigned short rdpsnd_opensles_millibel_to_volume(
int millibel, 
int max)
 
   71  const int min = SL_MILLIBEL_MIN;
 
   72  const int range = max - min;
 
   73  const int rc = ((millibel - min) * 0xFFFF + range / 2 + 1) / range;
 
   74  DEBUG_SND(
"millibel=%d, min=%d, max=%d, range=%d, result=%d", millibel, min, max, range, rc);
 
   78static bool rdpsnd_opensles_check_handle(
const rdpsndopenslesPlugin* hdl)
 
   93static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, UINT32 volume);
 
   95static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
 
   97  DEBUG_SND(
"opensles=%p", (
void*)opensles);
 
   99  if (!rdpsnd_opensles_check_handle(opensles))
 
  102  if (opensles->stream)
 
  103    android_CloseAudioDevice(opensles->stream);
 
  105  opensles->stream = android_OpenAudioDevice(opensles->rate, opensles->channels, 20);
 
  109static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device, 
const AUDIO_FORMAT* format,
 
  112  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  113  rdpsnd_opensles_check_handle(opensles);
 
  114  DEBUG_SND(
"opensles=%p format=%p, latency=%" PRIu32, (
void*)opensles, (
void*)format, latency);
 
  118    DEBUG_SND(
"format=%" PRIu16 
", cbsize=%" PRIu16 
", samples=%" PRIu32 
", bits=%" PRIu16
 
  119              ", channels=%" PRIu16 
", align=%" PRIu16 
"",
 
  120              format->wFormatTag, format->cbSize, format->nSamplesPerSec,
 
  121              format->wBitsPerSample, format->nChannels, format->nBlockAlign);
 
  122    opensles->rate = format->nSamplesPerSec;
 
  123    opensles->channels = format->nChannels;
 
  124    opensles->format = format->wFormatTag;
 
  125    opensles->wformat = format->wFormatTag;
 
  126    opensles->block_size = format->nBlockAlign;
 
  129  opensles->latency = latency;
 
  130  return (rdpsnd_opensles_set_params(opensles) == 0);
 
  133static BOOL rdpsnd_opensles_open(rdpsndDevicePlugin* device, 
const AUDIO_FORMAT* format,
 
  136  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  137  DEBUG_SND(
"opensles=%p format=%p, latency=%" PRIu32 
", rate=%" PRIu32 
"", (
void*)opensles,
 
  138            (
void*)format, latency, opensles->rate);
 
  140  if (rdpsnd_opensles_check_handle(opensles))
 
  143  opensles->stream = android_OpenAudioDevice(opensles->rate, opensles->channels, 20);
 
  144  WINPR_ASSERT(opensles->stream);
 
  146  if (!opensles->stream)
 
  147    WLog_ERR(TAG, 
"android_OpenAudioDevice failed");
 
  149    rdpsnd_opensles_set_volume(device, opensles->volume);
 
  151  return rdpsnd_opensles_set_format(device, format, latency);
 
  154static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
 
  156  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  157  DEBUG_SND(
"opensles=%p", (
void*)opensles);
 
  159  if (!rdpsnd_opensles_check_handle(opensles))
 
  162  android_CloseAudioDevice(opensles->stream);
 
  163  opensles->stream = NULL;
 
  166static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
 
  168  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  169  DEBUG_SND(
"opensles=%p", (
void*)opensles);
 
  170  WINPR_ASSERT(opensles);
 
  171  WINPR_ASSERT(opensles->device_name);
 
  172  free(opensles->device_name);
 
  176static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device, 
const AUDIO_FORMAT* format)
 
  178  DEBUG_SND(
"format=%" PRIu16 
", cbsize=%" PRIu16 
", samples=%" PRIu32 
", bits=%" PRIu16
 
  179            ", channels=%" PRIu16 
", align=%" PRIu16 
"",
 
  180            format->wFormatTag, format->cbSize, format->nSamplesPerSec, format->wBitsPerSample,
 
  181            format->nChannels, format->nBlockAlign);
 
  182  WINPR_ASSERT(device);
 
  183  WINPR_ASSERT(format);
 
  185  switch (format->wFormatTag)
 
  187    case WAVE_FORMAT_PCM:
 
  188      if (format->cbSize == 0 && format->nSamplesPerSec <= 48000 &&
 
  189          (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
 
  190          (format->nChannels == 1 || format->nChannels == 2))
 
  204static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device)
 
  206  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  207  DEBUG_SND(
"opensles=%p", (
void*)opensles);
 
  208  WINPR_ASSERT(opensles);
 
  210  if (opensles->stream)
 
  212    const int max = android_GetOutputVolumeMax(opensles->stream);
 
  213    const int rc = android_GetOutputVolume(opensles->stream);
 
  215    if (android_GetOutputMute(opensles->stream))
 
  216      opensles->volume = 0;
 
  219      const unsigned short vol = rdpsnd_opensles_millibel_to_volume(rc, max);
 
  220      opensles->volume = (vol << 16) | (vol & 0xFFFF);
 
  224  return opensles->volume;
 
  227static BOOL rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device, UINT32 value)
 
  229  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  230  DEBUG_SND(
"opensles=%p, value=%" PRIu32 
"", (
void*)opensles, value);
 
  231  WINPR_ASSERT(opensles);
 
  232  opensles->volume = value;
 
  234  if (opensles->stream)
 
  236    if (0 == opensles->volume)
 
  237      return android_SetOutputMute(opensles->stream, 
true);
 
  240      const int max = android_GetOutputVolumeMax(opensles->stream);
 
  241      const int vol = rdpsnd_opensles_volume_to_millibel(value & 0xFFFF, max);
 
  243      if (!android_SetOutputMute(opensles->stream, 
false))
 
  246      if (!android_SetOutputVolume(opensles->stream, vol))
 
  254static UINT rdpsnd_opensles_play(rdpsndDevicePlugin* device, 
const BYTE* data, 
size_t size)
 
  262  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  263  DEBUG_SND(
"opensles=%p, data=%p, size=%d", (
void*)opensles, (
void*)data, size);
 
  265  if (!rdpsnd_opensles_check_handle(opensles))
 
  269  DEBUG_SND(
"size=%d, src=%p", size, (
void*)src.b);
 
  270  WINPR_ASSERT(0 == size % 2);
 
  271  WINPR_ASSERT(size > 0);
 
  273  ret = android_AudioOut(opensles->stream, src.s, size / 2);
 
  276    WLog_ERR(TAG, 
"android_AudioOut failed (%d)", ret);
 
  281static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
 
  283  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  284  rdpsnd_opensles_check_handle(opensles);
 
  285  DEBUG_SND(
"opensles=%p", (
void*)opensles);
 
  288static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device, 
const ADDIN_ARGV* args)
 
  290  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)device;
 
  292    { 
"dev", COMMAND_LINE_VALUE_REQUIRED, 
"<device>", NULL, NULL, -1, NULL, 
"device" },
 
  293    { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
 
  296  WINPR_ASSERT(opensles);
 
  298  DEBUG_SND(
"opensles=%p, args=%p", (
void*)opensles, (
void*)args);
 
  300      COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
 
  301  const int status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_opensles_args,
 
  302                                                flags, opensles, NULL, NULL);
 
  311    if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
 
  314    CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, 
"dev")
 
  316      opensles->device_name = _strdup(arg->Value);
 
  318      if (!opensles->device_name)
 
  319        return ERROR_OUTOFMEMORY;
 
  321    CommandLineSwitchEnd(arg)
 
  322  } 
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
 
  332FREERDP_ENTRY_POINT(UINT VCAPITYPE opensles_freerdp_rdpsnd_client_subsystem_entry(
 
  335  UINT error = ERROR_INTERNAL_ERROR;
 
  336  DEBUG_SND(
"pEntryPoints=%p", (
void*)pEntryPoints);
 
  337  rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*)calloc(1, 
sizeof(rdpsndopenslesPlugin));
 
  340    return CHANNEL_RC_NO_MEMORY;
 
  342  opensles->device.Open = rdpsnd_opensles_open;
 
  343  opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
 
  344  opensles->device.GetVolume = rdpsnd_opensles_get_volume;
 
  345  opensles->device.SetVolume = rdpsnd_opensles_set_volume;
 
  346  opensles->device.Start = rdpsnd_opensles_start;
 
  347  opensles->device.Play = rdpsnd_opensles_play;
 
  348  opensles->device.Close = rdpsnd_opensles_close;
 
  349  opensles->device.Free = rdpsnd_opensles_free;
 
  351  rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*)opensles, args);
 
  353  if (!opensles->device_name)
 
  355    opensles->device_name = _strdup(
"default");
 
  357    if (!opensles->device_name)
 
  359      error = CHANNEL_RC_NO_MEMORY;
 
  364  opensles->rate = 44100;
 
  365  opensles->channels = 2;
 
  366  opensles->format = WAVE_FORMAT_ADPCM;
 
  367  pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)opensles);
 
  368  DEBUG_SND(
"success");
 
  369  return CHANNEL_RC_OK;