e0d13735c0a43adab3c5efc7640c3ebafa407551
[platform/upstream/freerdp.git] / channels / rdpsnd / client / opensles / rdpsnd_opensles.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Audio Output Virtual Channel
4  *
5  * Copyright 2013 Armin Novak <armin.novak@gmail.com>
6  *
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <assert.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdbool.h>
30
31 #include <winpr/crt.h>
32 #include <winpr/cmdline.h>
33 #include <winpr/sysinfo.h>
34 #include <winpr/collections.h>
35
36 #include <freerdp/types.h>
37 #include <freerdp/codec/dsp.h>
38 #include <freerdp/channels/log.h>
39
40 #include "opensl_io.h"
41 #include "rdpsnd_main.h"
42
43 typedef struct rdpsnd_opensles_plugin rdpsndopenslesPlugin;
44
45 struct rdpsnd_opensles_plugin
46 {
47         rdpsndDevicePlugin device;
48
49         int latency;
50         int wformat;
51         int block_size;
52         char* device_name;
53
54         OPENSL_STREAM *stream;
55
56         UINT32 volume;
57
58         UINT32 rate;
59         UINT32 channels;
60         int format;
61         FREERDP_DSP_CONTEXT* dsp_context;
62 };
63
64 static int rdpsnd_opensles_volume_to_millibel(unsigned short level, int max)
65 {
66         const int min = SL_MILLIBEL_MIN;
67         const int step = max - min;
68         const int rc = (level * step / 0xFFFF) + min;
69
70         DEBUG_SND("level=%d, min=%d, max=%d, step=%d, result=%d",
71                         level, min, max, step, rc);
72
73         return rc;
74 }
75
76 static unsigned short rdpsnd_opensles_millibel_to_volume(int millibel, int max)
77 {
78         const int min = SL_MILLIBEL_MIN;
79         const int range = max - min;
80         const int rc = ((millibel - min) * 0xFFFF + range / 2 + 1) / range;
81
82         DEBUG_SND("millibel=%d, min=%d, max=%d, range=%d, result=%d",
83                         millibel, min, max, range, rc);
84
85         return rc;
86 }
87
88 static bool rdpsnd_opensles_check_handle(const rdpsndopenslesPlugin *hdl)
89 {
90         bool rc = true;
91
92         if (!hdl)
93                 rc = false;
94         else
95         {
96                 if (!hdl->dsp_context)
97                         rc = false;
98                 if (!hdl->stream)
99                         rc = false;
100         }
101
102         return rc;
103 }
104
105 static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
106                 UINT32 volume);
107
108 static int rdpsnd_opensles_set_params(rdpsndopenslesPlugin* opensles)
109 {
110         DEBUG_SND("opensles=%p", opensles);
111         if (!rdpsnd_opensles_check_handle(opensles))
112                 return 0;
113
114         if (opensles->stream)
115                 android_CloseAudioDevice(opensles->stream);
116
117         opensles->stream = android_OpenAudioDevice(
118                 opensles->rate, opensles->channels, 20);
119
120         return 0;
121 }
122
123 static void rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
124                 AUDIO_FORMAT* format, int latency)
125 {
126         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
127         rdpsnd_opensles_check_handle(opensles);
128
129         DEBUG_SND("opensles=%p format=%p, latency=%d", opensles, format, latency);
130
131         if (format)
132         {
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);
136
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;
142         }
143
144         opensles->latency = latency;
145
146         rdpsnd_opensles_set_params(opensles);
147 }
148
149 static void rdpsnd_opensles_open(rdpsndDevicePlugin* device,
150                 AUDIO_FORMAT* format, int latency)
151 {
152         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
153
154         DEBUG_SND("opensles=%p format=%p, latency=%d, rate=%d",
155                         opensles, format, latency, opensles->rate);
156         
157         if( rdpsnd_opensles_check_handle(opensles))
158                 return;
159
160         opensles->stream = android_OpenAudioDevice(
161                 opensles->rate, opensles->channels, 20);
162         assert(opensles->stream);
163
164         if (!opensles->stream)
165                 WLog_ERR(TAG, "android_OpenAudioDevice failed");
166         else
167                 rdpsnd_opensles_set_volume(device, opensles->volume);
168
169         rdpsnd_opensles_set_format(device, format, latency);
170 }
171
172 static void rdpsnd_opensles_close(rdpsndDevicePlugin* device)
173 {
174         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
175
176         DEBUG_SND("opensles=%p", opensles);
177         if( !rdpsnd_opensles_check_handle(opensles))
178                 return;
179
180         android_CloseAudioDevice(opensles->stream);
181         opensles->stream = NULL;
182 }
183
184 static void rdpsnd_opensles_free(rdpsndDevicePlugin* device)
185 {
186         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
187
188         DEBUG_SND("opensles=%p", opensles);
189         assert(opensles);
190
191         assert(opensles->device_name);
192         free(opensles->device_name);
193
194         assert(opensles->dsp_context);
195         freerdp_dsp_context_free(opensles->dsp_context);
196
197         free(opensles);
198 }
199
200 static BOOL rdpsnd_opensles_format_supported(rdpsndDevicePlugin* device,
201                 AUDIO_FORMAT* format)
202 {
203         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
204
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);
208
209         assert(opensles);
210         assert(format);
211
212         switch (format->wFormatTag)
213         {
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))
219                         {
220                                 return TRUE;
221                         }
222                         break;
223
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))
229                         {
230                                 return TRUE;
231                         }
232                         break;
233
234                 case WAVE_FORMAT_ALAW:
235                 case WAVE_FORMAT_MULAW:
236                 case WAVE_FORMAT_GSM610:
237                 default:
238                         break;
239         }
240
241         return FALSE;
242 }
243
244 static UINT32 rdpsnd_opensles_get_volume(rdpsndDevicePlugin* device)
245 {
246         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
247
248         DEBUG_SND("opensles=%p", opensles);
249         assert(opensles);
250
251         if (opensles->stream)
252         {
253                 const int max = android_GetOutputVolumeMax(opensles->stream);
254                 const int rc = android_GetOutputVolume(opensles->stream);
255
256                 if (android_GetOutputMute(opensles->stream))
257                         opensles->volume = 0;
258                 else
259                 {
260                         const unsigned short vol = rdpsnd_opensles_millibel_to_volume(rc, max);
261                         opensles->volume = (vol << 16) | (vol & 0xFFFF);
262                 }
263         }
264                 
265         return opensles->volume;
266 }
267
268 static void rdpsnd_opensles_set_volume(rdpsndDevicePlugin* device,
269                 UINT32 value)
270 {
271         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
272         
273         DEBUG_SND("opensles=%p, value=%d", opensles, value);
274         assert(opensles);
275
276         opensles->volume = value;
277
278         if (opensles->stream)
279         {
280                 if (0 == opensles->volume)
281                         android_SetOutputMute(opensles->stream, true);
282                 else
283                 {
284                         const int max = android_GetOutputVolumeMax(opensles->stream);
285                         const int vol = rdpsnd_opensles_volume_to_millibel(value & 0xFFFF, max);
286
287                         android_SetOutputMute(opensles->stream, false);
288                         android_SetOutputVolume(opensles->stream, vol);
289                 }
290         }
291 }
292
293 static void rdpsnd_opensles_play(rdpsndDevicePlugin* device,
294                 BYTE *data, int size)
295 {
296         union
297         {
298                 BYTE *b;
299                 short *s;
300         } src;
301         int ret;
302         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
303         
304         DEBUG_SND("opensles=%p, data=%p, size=%d", opensles, data, size);
305         if (!rdpsnd_opensles_check_handle(opensles))
306                 return;
307
308         if (opensles->format == WAVE_FORMAT_ADPCM)
309         {
310                 DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d",
311                                 opensles->dsp_context, opensles->channels, opensles->block_size);
312
313                 opensles->dsp_context->decode_ms_adpcm(opensles->dsp_context,
314                                 data, size, opensles->channels, opensles->block_size);
315
316                 size = opensles->dsp_context->adpcm_size;
317                 src.b = opensles->dsp_context->adpcm_buffer;
318         }
319         else if (opensles->format == WAVE_FORMAT_DVI_ADPCM)
320         {
321                 DEBUG_SND("dsp_context=%p, channels=%d, block_size=%d",
322                                 opensles->dsp_context, opensles->channels, opensles->block_size);
323                 
324                 opensles->dsp_context->decode_ima_adpcm(opensles->dsp_context,
325                                 data, size, opensles->channels, opensles->block_size);
326                 
327                 size = opensles->dsp_context->adpcm_size;
328                 src.b = opensles->dsp_context->adpcm_buffer;
329         }
330         else
331         {   
332                 src.b = data;
333         } 
334
335         DEBUG_SND("size=%d, src=%p", size, src.b);
336         assert(0 == size % 2);
337         assert(size > 0);
338         assert(src.b);
339
340         ret = android_AudioOut(opensles->stream, src.s, size / 2);
341         if (ret < 0)
342                 WLog_ERR(TAG, "android_AudioOut failed (%d)", ret);
343 }
344
345 static void rdpsnd_opensles_start(rdpsndDevicePlugin* device)
346 {
347         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
348         rdpsnd_opensles_check_handle(opensles);
349
350         DEBUG_SND("opensles=%p", opensles);
351 }
352
353 static COMMAND_LINE_ARGUMENT_A rdpsnd_opensles_args[] =
354 {
355         { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
356                 NULL, NULL, -1, NULL, "device" },
357         { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
358 };
359
360 static int rdpsnd_opensles_parse_addin_args(rdpsndDevicePlugin* device,
361                 ADDIN_ARGV* args)
362 {
363         int status;
364         DWORD flags;
365         COMMAND_LINE_ARGUMENT_A* arg;
366         rdpsndopenslesPlugin* opensles = (rdpsndopenslesPlugin*) device;
367
368         assert(opensles);
369         assert(args);
370
371         DEBUG_SND("opensles=%p, args=%p", opensles, args);
372
373         flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
374
375         status = CommandLineParseArgumentsA(args->argc, (const char**) args->argv,
376                         rdpsnd_opensles_args, flags, opensles, NULL, NULL);
377         if (status < 0)
378                 return status;
379
380         arg = rdpsnd_opensles_args;
381
382         do
383         {
384                 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
385                         continue;
386
387                 CommandLineSwitchStart(arg)
388
389                 CommandLineSwitchCase(arg, "dev")
390                 {
391                         opensles->device_name = _strdup(arg->Value);
392                 }
393
394                 CommandLineSwitchEnd(arg)
395         }
396         while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
397
398         return status;
399 }
400
401 #ifdef STATIC_CHANNELS
402 #define freerdp_rdpsnd_client_subsystem_entry \
403         opensles_freerdp_rdpsnd_client_subsystem_entry
404 #endif
405
406 int freerdp_rdpsnd_client_subsystem_entry(
407                 PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
408 {
409         ADDIN_ARGV* args;
410         rdpsndopenslesPlugin* opensles;
411
412         DEBUG_SND("pEntryPoints=%p", pEntryPoints);
413
414         opensles = (rdpsndopenslesPlugin*) malloc(sizeof(rdpsndopenslesPlugin));
415         ZeroMemory(opensles, sizeof(rdpsndopenslesPlugin));
416
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;
426
427         args = pEntryPoints->args;
428         rdpsnd_opensles_parse_addin_args((rdpsndDevicePlugin*) opensles, args);
429
430         if (!opensles->device_name)
431                 opensles->device_name = _strdup("default");
432
433         opensles->rate = 44100;
434         opensles->channels = 2;
435         opensles->format = WAVE_FORMAT_ADPCM;
436
437         opensles->dsp_context = freerdp_dsp_context_new();
438
439         pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd,
440                         (rdpsndDevicePlugin*) opensles);
441
442         DEBUG_SND("success");
443         return 0;
444 }