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 } 320