1 /* -*- c-basic-offset: 8 -*- 2 rdesktop: A Remote Desktop Protocol client. 3 Sound Channel Process Functions - Sun 4 Copyright (C) Matthew Chapman 2003 5 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003 6 Copyright (C) Michael Gernoth mike@zerfleddert.de 2003 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License along 19 with this program; if not, write to the Free Software Foundation, Inc., 20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 21 */ 22 23 #include "rdesktop.h" 24 #include <unistd.h> 25 #include <fcntl.h> 26 #include <errno.h> 27 #include <sys/ioctl.h> 28 #include <sys/audioio.h> 29 30 #if (defined(sun) && (defined(__svr4__) || defined(__SVR4))) 31 #include <stropts.h> 32 #endif 33 34 #define MAX_QUEUE 10 35 36 int This->dsp_; 37 BOOL This->dsp_bu = False; 38 static BOOL g_reopened; 39 static BOOL g_swapaudio; 40 static short g_samplewidth; 41 42 static struct audio_packet 43 { 44 struct stream s; 45 uint16 tick; 46 uint8 index; 47 } packet_queue[MAX_QUEUE]; 48 static unsigned int queue_hi, queue_lo; 49 50 BOOL 51 wave_out_open(void) 52 { 53 char *dsp_dev = getenv("AUDIODEV"); 54 55 if (dsp_dev == NULL) 56 { 57 dsp_dev = xstrdup("/dev/audio"); 58 } 59 60 if ((This->dsp_ = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1) 61 { 62 perror(dsp_dev); 63 return False; 64 } 65 66 /* Non-blocking so that user interface is responsive */ 67 fcntl(This->dsp_, F_SETFL, fcntl(This->dsp_, F_GETFL) | O_NONBLOCK); 68 69 queue_lo = queue_hi = 0; 70 g_reopened = True; 71 72 return True; 73 } 74 75 void 76 wave_out_close(void) 77 { 78 /* Ack all remaining packets */ 79 while (queue_lo != queue_hi) 80 { 81 rdpsnd_send_completion(packet_queue[queue_lo].tick, packet_queue[queue_lo].index); 82 free(packet_queue[queue_lo].s.data); 83 queue_lo = (queue_lo + 1) % MAX_QUEUE; 84 } 85 86 #if defined I_FLUSH && defined FLUSHW 87 /* Flush the audiobuffer */ 88 ioctl(This->dsp_, I_FLUSH, FLUSHW); 89 #endif 90 #if defined AUDIO_FLUSH 91 ioctl(This->dsp_, AUDIO_FLUSH, NULL); 92 #endif 93 close(This->dsp_); 94 } 95 96 BOOL 97 wave_out_format_supported(WAVEFORMATEX * pwfx) 98 { 99 if (pwfx->wFormatTag != WAVE_FORMAT_PCM) 100 return False; 101 if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2)) 102 return False; 103 if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16)) 104 return False; 105 106 return True; 107 } 108 109 BOOL 110 wave_out_set_format(WAVEFORMATEX * pwfx) 111 { 112 audio_info_t info; 113 114 ioctl(This->dsp_, AUDIO_DRAIN, 0); 115 g_swapaudio = False; 116 AUDIO_INITINFO(&info); 117 118 119 if (pwfx->wBitsPerSample == 8) 120 { 121 info.play.encoding = AUDIO_ENCODING_LINEAR8; 122 } 123 else if (pwfx->wBitsPerSample == 16) 124 { 125 info.play.encoding = AUDIO_ENCODING_LINEAR; 126 /* Do we need to swap the 16bit values? (Are we BigEndian) */ 127 #ifdef B_ENDIAN 128 g_swapaudio = 1; 129 #else 130 g_swapaudio = 0; 131 #endif 132 } 133 134 g_samplewidth = pwfx->wBitsPerSample / 8; 135 136 if (pwfx->nChannels == 1) 137 { 138 info.play.channels = 1; 139 } 140 else if (pwfx->nChannels == 2) 141 { 142 info.play.channels = 2; 143 g_samplewidth *= 2; 144 } 145 146 info.play.sample_rate = pwfx->nSamplesPerSec; 147 info.play.precision = pwfx->wBitsPerSample; 148 info.play.samples = 0; 149 info.play.eof = 0; 150 info.play.error = 0; 151 g_reopened = True; 152 153 if (ioctl(This->dsp_, AUDIO_SETINFO, &info) == -1) 154 { 155 perror("AUDIO_SETINFO"); 156 close(This->dsp_); 157 return False; 158 } 159 160 return True; 161 } 162 163 void 164 wave_out_volume(uint16 left, uint16 right) 165 { 166 audio_info_t info; 167 uint balance; 168 uint volume; 169 170 AUDIO_INITINFO(&info); 171 172 volume = (left > right) ? left : right; 173 174 if (volume / AUDIO_MID_BALANCE != 0) 175 { 176 balance = 177 AUDIO_MID_BALANCE - (left / (volume / AUDIO_MID_BALANCE)) + 178 (right / (volume / AUDIO_MID_BALANCE)); 179 } 180 else 181 { 182 balance = AUDIO_MID_BALANCE; 183 } 184 185 info.play.gain = volume / (65536 / AUDIO_MAX_GAIN); 186 info.play.balance = balance; 187 188 if (ioctl(This->dsp_, AUDIO_SETINFO, &info) == -1) 189 { 190 perror("AUDIO_SETINFO"); 191 return; 192 } 193 } 194 195 void 196 wave_out_write(STREAM s, uint16 tick, uint8 index) 197 { 198 struct audio_packet *packet = &packet_queue[queue_hi]; 199 unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE; 200 201 if (next_hi == queue_lo) 202 { 203 error("No space to queue audio packet\n"); 204 return; 205 } 206 207 queue_hi = next_hi; 208 209 packet->s = *s; 210 packet->tick = tick; 211 packet->index = index; 212 packet->s.p += 4; 213 214 /* we steal the data buffer from s, give it a new one */ 215 s->data = malloc(s->size); 216 217 if (!This->dsp_bu) 218 wave_out_play(); 219 } 220 221 void 222 wave_out_play(void) 223 { 224 struct audio_packet *packet; 225 audio_info_t info; 226 ssize_t len; 227 unsigned int i; 228 uint8 swap; 229 STREAM out; 230 static BOOL swapped = False; 231 static BOOL sentcompletion = True; 232 static uint32 samplecnt = 0; 233 static uint32 numsamples; 234 235 while (1) 236 { 237 if (g_reopened) 238 { 239 /* Device was just (re)openend */ 240 samplecnt = 0; 241 swapped = False; 242 sentcompletion = True; 243 g_reopened = False; 244 } 245 246 if (queue_lo == queue_hi) 247 { 248 This->dsp_bu = 0; 249 return; 250 } 251 252 packet = &packet_queue[queue_lo]; 253 out = &packet->s; 254 255 /* Swap the current packet, but only once */ 256 if (g_swapaudio && !swapped) 257 { 258 for (i = 0; i < out->end - out->p; i += 2) 259 { 260 swap = *(out->p + i); 261 *(out->p + i) = *(out->p + i + 1); 262 *(out->p + i + 1) = swap; 263 } 264 swapped = True; 265 } 266 267 if (sentcompletion) 268 { 269 sentcompletion = False; 270 numsamples = (out->end - out->p) / g_samplewidth; 271 } 272 273 len = 0; 274 275 if (out->end != out->p) 276 { 277 len = write(This->dsp_, out->p, out->end - out->p); 278 if (len == -1) 279 { 280 if (errno != EWOULDBLOCK) 281 perror("write audio"); 282 This->dsp_bu = 1; 283 return; 284 } 285 } 286 287 out->p += len; 288 if (out->p == out->end) 289 { 290 if (ioctl(This->dsp_, AUDIO_GETINFO, &info) == -1) 291 { 292 perror("AUDIO_GETINFO"); 293 return; 294 } 295 296 /* Ack the packet, if we have played at least 70% */ 297 if (info.play.samples >= samplecnt + ((numsamples * 7) / 10)) 298 { 299 samplecnt += numsamples; 300 rdpsnd_send_completion(packet->tick, packet->index); 301 free(out->data); 302 queue_lo = (queue_lo + 1) % MAX_QUEUE; 303 swapped = False; 304 sentcompletion = True; 305 } 306 else 307 { 308 This->dsp_bu = 1; 309 return; 310 } 311 } 312 } 313 } 314