2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Audio Output Virtual Channel
5 * Copyright 2013 Armin Novak <armin.novak@gmail.com>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
31 #include <winpr/crt.h>
32 #include <winpr/cmdline.h>
33 #include <winpr/sysinfo.h>
34 #include <winpr/collections.h>
36 #include <freerdp/types.h>
37 #include <freerdp/codec/dsp.h>
38 #include <freerdp/channels/log.h>
40 #include "opensl_io.h"
41 #include "rdpsnd_main.h"
43 typedef struct rdpsnd_opensles_plugin rdpsndopenslesPlugin;
45 struct rdpsnd_opensles_plugin
47 rdpsndDevicePlugin device;
54 OPENSL_STREAM *stream;
61 FREERDP_DSP_CONTEXT* dsp_context;
64 static int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max)
66 const int min = SL_MILLIBEL_MIN;
67 const int step = max - min;
68 const int rc = (level * step / 0xFFFF) + min;
70 DEBUG_SND("level=%d, min=%d, max=%d, step=%d, result=%d",
71 level, min, max, step, rc);
76 static unsigned short rdpsnd_opensles_millibel_to_volume(int millibel, int max)
78 const int min = SL_MILLIBEL_MIN;
79 const int range = max - min;
80 const int rc = ((millibel - min) * 0xFFFF + range / 2 + 1) / range;
82 DEBUG_SND("millibel=%d, min=%d, max=%d, range=%d, result=%d",
83 millibel, min, max, range, rc);
88 static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin *hdl)
96 if (!hdl->dsp_context)
105 static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
108 static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
110 DEBUG_SND("opensles=%p", opensles);
111 if (!rdpsnd_opensles_check_handle(opensles))
114 if (opensles->stream)
115 android_CloseAudioDevice(opensles->stream);
117 opensles->stream = android_OpenAudioDevice(
118 opensles->rate, opensles->channels, 20);
123 static void rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
124 AUDIO_FORMAT* format, int latency)
126 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
127 rdpsnd_opensles_check_handle(opensles);
129 DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency);
133 DEBUG_SND("format=%d, cbsize=%d, samples=%d, bits=%d, channels=%d, align=%d",
134 format->wFormatTag, format->cbSize, format->nSamplesPerSec,
135 format->wBitsPerSample, format->nChannels, format->nBlockAlign);
137 opensles->rate = format->nSamplesPerSec;
138 opensles->channels = format->nChannels;
139 opensles->format = format->wFormatTag;
140 opensles->wformat = format->wFormatTag;
141 opensles->block_size = format->nBlockAlign;
144 opensles->latency = latency;
146 rdpsnd_opensles_set_params(opensles);
149 static void rdpsnd_opensles_open(rdpsndDevicePlugin* device,
150 AUDIO_FORMAT* format, int latency)
152 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
154 DEBUG_SND("opensles=%p format=%p, latency=%d, rate=%d",
155 opensles, format, latency, opensles->rate);
157 if( rdpsnd_opensles_check_handle(opensles))
160 opensles->stream = android_OpenAudioDevice(
161 opensles->rate, opensles->channels, 20);
162 assert(opensles->stream);
164 if (!opensles->stream)
165 WLog_ERR(TAG, "android_OpenAudioDevice failed");
167 rdpsnd_opensles_set_volume(device, opensles->volume);
169 rdpsnd_opensles_set_format(device, format, latency);
172 static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
174 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
176 DEBUG_SND("opensles=%p", opensles);
177 if( !rdpsnd_opensles_check_handle(opensles))
180 android_CloseAudioDevice(opensles->stream);
181 opensles->stream = NULL;
184 static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
186 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
188 DEBUG_SND("opensles=%p", opensles);
191 assert(opensles->device_name);
192 free(opensles->device_name);
194 assert(opensles->dsp_context);
195 freerdp_dsp_context_free(opensles->dsp_context);
200 static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
201 AUDIO_FORMAT* format)
203 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
205 DEBUG_SND("format=%d, cbsize=%d, samples=%d, bits=%d, channels=%d, align=%d",
206 format->wFormatTag, format->cbSize, format->nSamplesPerSec,
207 format->wBitsPerSample, format->nChannels, format->nBlockAlign);
212 switch (format->wFormatTag)
214 case WAVE_FORMAT_PCM:
215 if (format->cbSize == 0 &&
216 format->nSamplesPerSec <= 48000 &&
217 (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
218 (format->nChannels == 1 || format->nChannels == 2))
224 case WAVE_FORMAT_ADPCM:
225 case WAVE_FORMAT_DVI_ADPCM:
226 if (format->nSamplesPerSec <= 48000 &&
227 format->wBitsPerSample == 4 &&
228 (format->nChannels == 1 || format->nChannels == 2))
234 case WAVE_FORMAT_ALAW:
235 case WAVE_FORMAT_MULAW:
236 case WAVE_FORMAT_GSM610:
244 static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device)
246 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
248 DEBUG_SND("opensles=%p", opensles);
251 if (opensles->stream)
253 const int max = android_GetOutputVolumeMax(opensles->stream);
254 const int rc = android_GetOutputVolume(opensles->stream);
256 if (android_GetOutputMute(opensles->stream))
257 opensles->volume = 0;
260 const unsigned short vol = rdpsnd_opensles_millibel_to_volume(rc, max);
261 opensles->volume = (vol << 16) | (vol & 0xFFFF);
265 return opensles->volume;
268 static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
271 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
273 DEBUG_SND("opensles=%p, value=%d", opensles, value);
276 opensles->volume = value;
278 if (opensles->stream)
280 if (0 == opensles->volume)
281 android_SetOutputMute(opensles->stream, true);
284 const int max = android_GetOutputVolumeMax(opensles->stream);
285 const int vol = rdpsnd_opensles_volume_to_millibel(value & 0xFFFF, max);
287 android_SetOutputMute(opensles->stream, false);
288 android_SetOutputVolume(opensles->stream, vol);
293 static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
294 BYTE *data, int size)
302 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
304 DEBUG_SND("opensles=%p, data=%p, size=%d", opensles, data, size);
305 if (!rdpsnd_opensles_check_handle(opensles))
308 if (opensles->format == WAVE_FORMAT_ADPCM)
310 DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d",
311 opensles->dsp_context, opensles->channels, opensles->block_size);
313 opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context,
314 data, size, opensles->channels, opensles->block_size);
316 size = opensles->dsp_context->adpcm_size;
317 src.b = opensles->dsp_context->adpcm_buffer;
319 else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
321 DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d",
322 opensles->dsp_context, opensles->channels, opensles->block_size);
324 opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context,
325 data, size, opensles->channels, opensles->block_size);
327 size = opensles->dsp_context->adpcm_size;
328 src.b = opensles->dsp_context->adpcm_buffer;
335 DEBUG_SND("size=%d, src=%p", size, src.b);
336 assert(0 == size % 2);
340 ret = android_AudioOut(opensles->stream, src.s, size / 2);
342 WLog_ERR(TAG, "android_AudioOut failed (%d)", ret);
345 static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
347 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
348 rdpsnd_opensles_check_handle(opensles);
350 DEBUG_SND("opensles=%p", opensles);
353 static COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] =
355 { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
356 NULL, NULL, -1, NULL, "device" },
357 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
360 static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device,
365 COMMAND_LINE_ARGUMENT_A* arg;
366 rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
371 DEBUG_SND("opensles=%p, args=%p", opensles, args);
373 flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
375 status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
376 rdpsnd_opensles_args, flags, opensles, NULL, NULL);
380 arg = rdpsnd_opensles_args;
384 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
387 CommandLineSwitchStart(arg)
389 CommandLineSwitchCase(arg, "dev")
391 opensles->device_name = _strdup(arg->Value);
394 CommandLineSwitchEnd(arg)
396 while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
401 #ifdef STATIC_CHANNELS
402 #define freerdp_rdpsnd_client_subsystem_entry \
403 opensles_freerdp_rdpsnd_client_subsystem_entry
406 int freerdp_rdpsnd_client_subsystem_entry(
407 PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
410 rdpsndopenslesPlugin* opensles;
412 DEBUG_SND("pEntryPoints=%p", pEntryPoints);
414 opensles = (rdpsndopenslesPlugin*) malloc(sizeof(rdpsndopenslesPlugin));
415 ZeroMemory(opensles, sizeof(rdpsndopenslesPlugin));
417 opensles->device.Open = rdpsnd_opensles_open;
418 opensles->device.FormatSupported = rdpsnd_opensles_format_supported;
419 opensles->device.SetFormat = rdpsnd_opensles_set_format;
420 opensles->device.GetVolume = rdpsnd_opensles_get_volume;
421 opensles->device.SetVolume = rdpsnd_opensles_set_volume;
422 opensles->device.Start = rdpsnd_opensles_start;
423 opensles->device.Play = rdpsnd_opensles_play;
424 opensles->device.Close = rdpsnd_opensles_close;
425 opensles->device.Free = rdpsnd_opensles_free;
427 args = pEntryPoints->args;
428 rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*) opensles, args);
430 if (!opensles->device_name)
431 opensles->device_name = _strdup("default");
433 opensles->rate = 44100;
434 opensles->channels = 2;
435 opensles->format = WAVE_FORMAT_ADPCM;
437 opensles->dsp_context = freerdp_dsp_context_new();
439 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd,
440 (rdpsndDevicePlugin*) opensles);
442 DEBUG_SND("success");