Incorporate rosapps. 0.3.15 was branched somewhat incorrectly so rosapps is not synce...
[reactos.git] / modules / rosapps / applications / net / tsclient / rdesktop / rdpsnd_oss.c
1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Sound Channel Process Functions - Open Sound System
4 Copyright (C) Matthew Chapman 2003
5 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 /*
23 This is a workaround for Esound bug 312665.
24 FIXME: Remove this when Esound is fixed.
25 */
26 #ifdef _FILE_OFFSET_BITS
27 #undef _FILE_OFFSET_BITS
28 #endif
29
30 #include "rdesktop.h"
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/time.h>
35 #include <sys/ioctl.h>
36 #include <sys/soundcard.h>
37
38 #define MAX_QUEUE 10
39
40 int This->dsp_;
41 BOOL This->dsp_bu = False;
42 static int g_snd_rate;
43 static short g_samplewidth;
44 static BOOL g_driver_broken = False;
45
46 static struct audio_packet
47 {
48 struct stream s;
49 uint16 tick;
50 uint8 index;
51 } packet_queue[MAX_QUEUE];
52 static unsigned int queue_hi, queue_lo;
53
54 BOOL
55 wave_out_open(void)
56 {
57 char *dsp_dev = getenv("AUDIODEV");
58
59 if (dsp_dev == NULL)
60 {
61 dsp_dev = xstrdup("/dev/dsp");
62 }
63
64 if ((This->dsp_ = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1)
65 {
66 perror(dsp_dev);
67 return False;
68 }
69
70 /* Non-blocking so that user interface is responsive */
71 fcntl(This->dsp_, F_SETFL, fcntl(This->dsp_, F_GETFL) | O_NONBLOCK);
72 return True;
73 }
74
75 void
76 wave_out_close(void)
77 {
78 close(This->dsp_);
79 }
80
81 BOOL
82 wave_out_format_supported(WAVEFORMATEX * pwfx)
83 {
84 if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
85 return False;
86 if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2))
87 return False;
88 if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
89 return False;
90
91 return True;
92 }
93
94 BOOL
95 wave_out_set_format(WAVEFORMATEX * pwfx)
96 {
97 int stereo, format, fragments;
98
99 ioctl(This->dsp_, SNDCTL_DSP_RESET, NULL);
100 ioctl(This->dsp_, SNDCTL_DSP_SYNC, NULL);
101
102 if (pwfx->wBitsPerSample == 8)
103 format = AFMT_U8;
104 else if (pwfx->wBitsPerSample == 16)
105 format = AFMT_S16_LE;
106
107 g_samplewidth = pwfx->wBitsPerSample / 8;
108
109 if (ioctl(This->dsp_, SNDCTL_DSP_SETFMT, &format) == -1)
110 {
111 perror("SNDCTL_DSP_SETFMT");
112 close(This->dsp_);
113 return False;
114 }
115
116 if (pwfx->nChannels == 2)
117 {
118 stereo = 1;
119 g_samplewidth *= 2;
120 }
121 else
122 {
123 stereo = 0;
124 }
125
126 if (ioctl(This->dsp_, SNDCTL_DSP_STEREO, &stereo) == -1)
127 {
128 perror("SNDCTL_DSP_CHANNELS");
129 close(This->dsp_);
130 return False;
131 }
132
133 g_snd_rate = pwfx->nSamplesPerSec;
134 if (ioctl(This->dsp_, SNDCTL_DSP_SPEED, &g_snd_rate) == -1)
135 {
136 perror("SNDCTL_DSP_SPEED");
137 close(This->dsp_);
138 return False;
139 }
140
141 /* try to get 7 fragments of 2^12 bytes size */
142 fragments = (7 << 16) + 12;
143 ioctl(This->dsp_, SNDCTL_DSP_SETFRAGMENT, &fragments);
144
145 if (!g_driver_broken)
146 {
147 audio_buf_info info;
148
149 memset(&info, 0, sizeof(info));
150 if (ioctl(This->dsp_, SNDCTL_DSP_GETOSPACE, &info) == -1)
151 {
152 perror("SNDCTL_DSP_GETOSPACE");
153 close(This->dsp_);
154 return False;
155 }
156
157 if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
158 {
159 fprintf(stderr,
160 "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
161 info.fragments, info.fragstotal, info.fragsize);
162 g_driver_broken = True;
163 }
164 }
165
166 return True;
167 }
168
169 void
170 wave_out_volume(uint16 left, uint16 right)
171 {
172 static BOOL use_dev_mixer = False;
173 uint32 volume;
174 int fd_mix = -1;
175
176 volume = left / (65536 / 100);
177 volume |= right / (65536 / 100) << 8;
178
179 if (use_dev_mixer)
180 {
181 if ((fd_mix = open("/dev/mixer", O_RDWR | O_NONBLOCK)) == -1)
182 {
183 perror("open /dev/mixer");
184 return;
185 }
186
187 if (ioctl(fd_mix, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
188 {
189 perror("MIXER_WRITE(SOUND_MIXER_PCM)");
190 return;
191 }
192
193 close(fd_mix);
194 }
195
196 if (ioctl(This->dsp_, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
197 {
198 perror("MIXER_WRITE(SOUND_MIXER_PCM)");
199 use_dev_mixer = True;
200 return;
201 }
202 }
203
204 void
205 wave_out_write(STREAM s, uint16 tick, uint8 index)
206 {
207 struct audio_packet *packet = &packet_queue[queue_hi];
208 unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;
209
210 if (next_hi == queue_lo)
211 {
212 error("No space to queue audio packet\n");
213 return;
214 }
215
216 queue_hi = next_hi;
217
218 packet->s = *s;
219 packet->tick = tick;
220 packet->index = index;
221 packet->s.p += 4;
222
223 /* we steal the data buffer from s, give it a new one */
224 s->data = (uint8 *) malloc(s->size);
225
226 if (!This->dsp_bu)
227 wave_out_play();
228 }
229
230 void
231 wave_out_play(void)
232 {
233 struct audio_packet *packet;
234 ssize_t len;
235 STREAM out;
236 static long startedat_us;
237 static long startedat_s;
238 static BOOL started = False;
239 struct timeval tv;
240 audio_buf_info info;
241
242 while (1)
243 {
244 if (queue_lo == queue_hi)
245 {
246 This->dsp_bu = 0;
247 return;
248 }
249
250 packet = &packet_queue[queue_lo];
251 out = &packet->s;
252
253 if (!started)
254 {
255 gettimeofday(&tv, NULL);
256 startedat_us = tv.tv_usec;
257 startedat_s = tv.tv_sec;
258 started = True;
259 }
260
261 len = out->end - out->p;
262
263 if (!g_driver_broken)
264 {
265 memset(&info, 0, sizeof(info));
266 if (ioctl(This->dsp_, SNDCTL_DSP_GETOSPACE, &info) == -1)
267 {
268 perror("SNDCTL_DSP_GETOSPACE");
269 return;
270 }
271
272 if (info.fragments == 0)
273 {
274 This->dsp_bu = 1;
275 return;
276 }
277
278 if (info.fragments * info.fragsize < len
279 && info.fragments * info.fragsize > 0)
280 {
281 len = info.fragments * info.fragsize;
282 }
283 }
284
285
286 len = write(This->dsp_, out->p, len);
287 if (len == -1)
288 {
289 if (errno != EWOULDBLOCK)
290 perror("write audio");
291 This->dsp_bu = 1;
292 return;
293 }
294
295 out->p += len;
296 if (out->p == out->end)
297 {
298 long long duration;
299 long elapsed;
300
301 gettimeofday(&tv, NULL);
302 duration = (out->size * (1000000 / (g_samplewidth * g_snd_rate)));
303 elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us);
304
305 if (elapsed >= (duration * 85) / 100)
306 {
307 rdpsnd_send_completion(packet->tick, packet->index);
308 free(out->data);
309 queue_lo = (queue_lo + 1) % MAX_QUEUE;
310 started = False;
311 }
312 else
313 {
314 This->dsp_bu = 1;
315 return;
316 }
317 }
318 }
319 }