FreeRDP
Loading...
Searching...
No Matches
rdpsnd_sndio.c
1
22#include <freerdp/config.h>
23
24#include <sndio.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <winpr/crt.h>
30#include <winpr/stream.h>
31#include <winpr/cmdline.h>
32
33#include <freerdp/types.h>
34
35#include "rdpsnd_main.h"
36
37typedef struct
38{
39 rdpsndDevicePlugin device;
40
41 struct sio_hdl* hdl;
42 struct sio_par par;
43} rdpsndSndioPlugin;
44
45static BOOL rdpsnd_sndio_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format,
46 WINPR_ATTR_UNUSED UINT32 latency)
47{
48 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
49
50 if (device == nullptr || format == nullptr)
51 return FALSE;
52
53 if (sndio->hdl != nullptr)
54 return TRUE;
55
56 sndio->hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0);
57 if (sndio->hdl == nullptr)
58 {
59 WLog_ERR(TAG, "could not open audio device");
60 return FALSE;
61 }
62
63 sio_initpar(&sndio->par);
64 sndio->par.bits = format->wBitsPerSample;
65 sndio->par.pchan = format->nChannels;
66 sndio->par.rate = format->nSamplesPerSec;
67 if (!sio_setpar(sndio->hdl, &sndio->par))
68 {
69 WLog_ERR(TAG, "could not set audio parameters");
70 return FALSE;
71 }
72 if (!sio_getpar(sndio->hdl, &sndio->par))
73 {
74 WLog_ERR(TAG, "could not get audio parameters");
75 return FALSE;
76 }
77
78 if (!sio_start(sndio->hdl))
79 {
80 WLog_ERR(TAG, "could not start audio device");
81 return FALSE;
82 }
83
84 return TRUE;
85}
86
87static void rdpsnd_sndio_close(rdpsndDevicePlugin* device)
88{
89 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
90
91 if (device == nullptr)
92 return;
93
94 if (sndio->hdl != nullptr)
95 {
96 sio_stop(sndio->hdl);
97 sio_close(sndio->hdl);
98 sndio->hdl = nullptr;
99 }
100}
101
102static BOOL rdpsnd_sndio_set_volume(rdpsndDevicePlugin* device, UINT32 value)
103{
104 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
105
106 if (device == nullptr || sndio->hdl == nullptr)
107 return FALSE;
108
109 /*
110 * Low-order word contains the left-channel volume setting.
111 * We ignore the right-channel volume setting in the high-order word.
112 */
113 return sio_setvol(sndio->hdl, ((value & 0xFFFF) * SIO_MAXVOL) / 0xFFFF);
114}
115
116static void rdpsnd_sndio_free(rdpsndDevicePlugin* device)
117{
118 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
119
120 if (device == nullptr)
121 return;
122
123 rdpsnd_sndio_close(device);
124 free(sndio);
125}
126
127static BOOL rdpsnd_sndio_format_supported(WINPR_ATTR_UNUSED rdpsndDevicePlugin* device,
128 const AUDIO_FORMAT* format)
129{
130 if (format == nullptr)
131 return FALSE;
132
133 return (format->wFormatTag == WAVE_FORMAT_PCM);
134}
135
136static UINT rdpsnd_sndio_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size)
137{
138 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
139
140 if (device == nullptr || sndio->hdl == nullptr)
141 return ERROR_INVALID_PARAMETER;
142
143 const size_t rc = sio_write(sndio->hdl, data, size);
144 if (rc != size)
145 return CHANNEL_RC_OK;
146 return CHANNEL_RC_OK;
147}
148
154static UINT rdpsnd_sndio_parse_addin_args(rdpsndDevicePlugin* device, const ADDIN_ARGV* args)
155{
156 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)device;
157 WINPR_ASSERT(sndio);
158
159 COMMAND_LINE_ARGUMENT_A rdpsnd_sndio_args[] = { { nullptr, 0, nullptr, nullptr, nullptr, -1,
160 nullptr, nullptr } };
161 const DWORD flags =
162 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
163 const int status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_sndio_args, flags,
164 sndio, nullptr, nullptr);
165
166 if (status < 0)
167 return ERROR_INVALID_DATA;
168
169 const COMMAND_LINE_ARGUMENT_A* arg = rdpsnd_sndio_args;
170
171 do
172 {
173 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
174 continue;
175
176 CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
177 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
178
179 return CHANNEL_RC_OK;
180}
181
187FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_rdpsnd_client_subsystem_entry(
189{
190 UINT ret = CHANNEL_RC_OK;
191 rdpsndSndioPlugin* sndio = (rdpsndSndioPlugin*)calloc(1, sizeof(rdpsndSndioPlugin));
192
193 if (sndio == nullptr)
194 return CHANNEL_RC_NO_MEMORY;
195
196 sndio->device.Open = rdpsnd_sndio_open;
197 sndio->device.FormatSupported = rdpsnd_sndio_format_supported;
198 sndio->device.SetVolume = rdpsnd_sndio_set_volume;
199 sndio->device.Play = rdpsnd_sndio_play;
200 sndio->device.Close = rdpsnd_sndio_close;
201 sndio->device.Free = rdpsnd_sndio_free;
202 const ADDIN_ARGV* args = pEntryPoints->args;
203
204 if (args->argc > 1)
205 {
206 ret = rdpsnd_sndio_parse_addin_args(&sndio->device, args);
207
208 if (ret != CHANNEL_RC_OK)
209 {
210 WLog_ERR(TAG, "error parsing arguments");
211 goto error;
212 }
213 }
214
215 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, &sndio->device);
216 return ret;
217error:
218 rdpsnd_sndio_free(&sndio->device);
219 return ret;
220}