FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
tsmf_oss.c
1
20#include <freerdp/config.h>
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26
27#include <winpr/crt.h>
28
29#include <err.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <libgen.h>
33#include <limits.h>
34#include <unistd.h>
35#if defined(__OpenBSD__)
36#include <soundcard.h>
37#else
38#include <sys/soundcard.h>
39#endif
40#include <sys/ioctl.h>
41
42#include <freerdp/types.h>
43#include <freerdp/codec/dsp.h>
44
45#include "tsmf_audio.h"
46
47typedef struct
48{
49 ITSMFAudioDevice iface;
50
51 char dev_name[PATH_MAX];
52 int pcm_handle;
53
54 UINT32 sample_rate;
55 UINT32 channels;
56 UINT32 bits_per_sample;
57
58 UINT32 data_size_last;
59} TSMFOssAudioDevice;
60
61#define OSS_LOG_ERR(_text, _error) \
62 do \
63 { \
64 if ((_error) != 0) \
65 { \
66 char ebuffer[256] = { 0 }; \
67 WLog_ERR(TAG, "%s: %i - %s", (_text), (_error), \
68 winpr_strerror((_error), ebuffer, sizeof(ebuffer))); \
69 } \
70 } while (0)
71
72static BOOL tsmf_oss_open(ITSMFAudioDevice* audio, const char* device)
73{
74 int tmp = 0;
75 TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
76
77 if (oss == NULL || oss->pcm_handle != -1)
78 return FALSE;
79
80 if (device == NULL) /* Default device. */
81 {
82 strncpy(oss->dev_name, "/dev/dsp", sizeof(oss->dev_name));
83 }
84 else
85 {
86 strncpy(oss->dev_name, device, sizeof(oss->dev_name) - 1);
87 }
88
89 if ((oss->pcm_handle = open(oss->dev_name, O_WRONLY)) < 0)
90 {
91 OSS_LOG_ERR("sound dev open failed", errno);
92 oss->pcm_handle = -1;
93 return FALSE;
94 }
95
96 const int rc = ioctl(oss->pcm_handle, SNDCTL_DSP_GETFMTS, &tmp);
97 if (rc == -1)
98 {
99 OSS_LOG_ERR("SNDCTL_DSP_GETFMTS failed", errno);
100 close(oss->pcm_handle);
101 oss->pcm_handle = -1;
102 return FALSE;
103 }
104
105 if ((AFMT_S16_LE & tmp) == 0)
106 {
107 OSS_LOG_ERR("SNDCTL_DSP_GETFMTS - AFMT_S16_LE", EOPNOTSUPP);
108 close(oss->pcm_handle);
109 oss->pcm_handle = -1;
110 return FALSE;
111 }
112
113 WLog_INFO(TAG, "open: %s", oss->dev_name);
114 return TRUE;
115}
116
117static BOOL tsmf_oss_set_format(ITSMFAudioDevice* audio, UINT32 sample_rate, UINT32 channels,
118 UINT32 bits_per_sample)
119{
120 int tmp = 0;
121 TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
122
123 if (oss == NULL || oss->pcm_handle == -1)
124 return FALSE;
125
126 oss->sample_rate = sample_rate;
127 oss->channels = channels;
128 oss->bits_per_sample = bits_per_sample;
129 tmp = AFMT_S16_LE;
130
131 if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
132 OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
133
134 tmp = channels;
135
136 if (ioctl(oss->pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
137 OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
138
139 tmp = sample_rate;
140
141 if (ioctl(oss->pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
142 OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
143
144 tmp = ((bits_per_sample / 8) * channels * sample_rate);
145
146 if (ioctl(oss->pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
147 OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
148
149 DEBUG_TSMF("sample_rate %" PRIu32 " channels %" PRIu32 " bits_per_sample %" PRIu32 "",
150 sample_rate, channels, bits_per_sample);
151 return TRUE;
152}
153
154static BOOL tsmf_oss_play(ITSMFAudioDevice* audio, const BYTE* data, UINT32 data_size)
155{
156 UINT32 offset = 0;
157 TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
158 DEBUG_TSMF("tsmf_oss_play: data_size %" PRIu32 "", data_size);
159
160 if (oss == NULL || oss->pcm_handle == -1)
161 return FALSE;
162
163 if (data == NULL || data_size == 0)
164 return TRUE;
165
166 offset = 0;
167 oss->data_size_last = data_size;
168
169 while (offset < data_size)
170 {
171 const ssize_t status = write(oss->pcm_handle, &data[offset], (data_size - offset));
172
173 if (status < 0)
174 {
175 OSS_LOG_ERR("write fail", errno);
176 return FALSE;
177 }
178
179 offset += status;
180 }
181
182 return TRUE;
183}
184
185static UINT64 tsmf_oss_get_latency(ITSMFAudioDevice* audio)
186{
187 UINT64 latency = 0;
188 TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
189
190 if (oss == NULL)
191 return 0;
192
193 // latency = ((oss->data_size_last / (oss->bits_per_sample / 8)) * oss->sample_rate);
194 // WLog_INFO(TAG, "latency: %zu", latency);
195 return latency;
196}
197
198static BOOL tsmf_oss_flush(ITSMFAudioDevice* audio)
199{
200 return TRUE;
201}
202
203static void tsmf_oss_free(ITSMFAudioDevice* audio)
204{
205 TSMFOssAudioDevice* oss = (TSMFOssAudioDevice*)audio;
206
207 if (oss == NULL)
208 return;
209
210 if (oss->pcm_handle != -1)
211 {
212 WLog_INFO(TAG, "close: %s", oss->dev_name);
213 close(oss->pcm_handle);
214 }
215
216 free(oss);
217}
218
219FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_tsmf_client_audio_subsystem_entry(void* ptr))
220{
221 ITSMFAudioDevice** sptr = (ITSMFAudioDevice**)ptr;
222 WINPR_ASSERT(sptr);
223 *sptr = NULL;
224
225 TSMFOssAudioDevice* oss = calloc(1, sizeof(TSMFOssAudioDevice));
226 if (!oss)
227 return ERROR_OUTOFMEMORY;
228
229 oss->iface.Open = tsmf_oss_open;
230 oss->iface.SetFormat = tsmf_oss_set_format;
231 oss->iface.Play = tsmf_oss_play;
232 oss->iface.GetLatency = tsmf_oss_get_latency;
233 oss->iface.Flush = tsmf_oss_flush;
234 oss->iface.Free = tsmf_oss_free;
235 oss->pcm_handle = -1;
236 *sptr = &oss->iface;
237 return CHANNEL_RC_OK;
238}