SDL_netbsdaudio.c (9709B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 #include "../../SDL_internal.h" 22 23 #if SDL_AUDIO_DRIVER_NETBSD 24 25 /* 26 * Driver for native NetBSD audio(4). 27 * nia@NetBSD.org 28 */ 29 30 #include <errno.h> 31 #include <unistd.h> 32 #include <fcntl.h> 33 #include <sys/time.h> 34 #include <sys/ioctl.h> 35 #include <sys/stat.h> 36 #include <sys/types.h> 37 #include <sys/audioio.h> 38 39 #include "SDL_timer.h" 40 #include "SDL_audio.h" 41 #include "../../core/unix/SDL_poll.h" 42 #include "../SDL_audio_c.h" 43 #include "../SDL_audiodev_c.h" 44 #include "SDL_netbsdaudio.h" 45 46 /* #define DEBUG_AUDIO */ 47 48 static void 49 NETBSDAUDIO_DetectDevices(void) 50 { 51 SDL_EnumUnixAudioDevices(0, NULL); 52 } 53 54 55 static void 56 NETBSDAUDIO_Status(_THIS) 57 { 58 #ifdef DEBUG_AUDIO 59 /* *INDENT-OFF* */ 60 audio_info_t info; 61 const struct audio_prinfo *prinfo; 62 63 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 64 fprintf(stderr, "AUDIO_GETINFO failed.\n"); 65 return; 66 } 67 68 prinfo = this->iscapture ? &info.record : &info.play; 69 70 fprintf(stderr, "\n" 71 "[%s info]\n" 72 "buffer size : %d bytes\n" 73 "sample rate : %i Hz\n" 74 "channels : %i\n" 75 "precision : %i-bit\n" 76 "encoding : 0x%x\n" 77 "seek : %i\n" 78 "sample count : %i\n" 79 "EOF count : %i\n" 80 "paused : %s\n" 81 "error occured : %s\n" 82 "waiting : %s\n" 83 "active : %s\n" 84 "", 85 this->iscapture ? "record" : "play", 86 prinfo->buffer_size, 87 prinfo->sample_rate, 88 prinfo->channels, 89 prinfo->precision, 90 prinfo->encoding, 91 prinfo->seek, 92 prinfo->samples, 93 prinfo->eof, 94 prinfo->pause ? "yes" : "no", 95 prinfo->error ? "yes" : "no", 96 prinfo->waiting ? "yes" : "no", 97 prinfo->active ? "yes" : "no"); 98 99 fprintf(stderr, "\n" 100 "[audio info]\n" 101 "monitor_gain : %i\n" 102 "hw block size : %d bytes\n" 103 "hi watermark : %i\n" 104 "lo watermark : %i\n" 105 "audio mode : %s\n" 106 "", 107 info.monitor_gain, 108 info.blocksize, 109 info.hiwat, info.lowat, 110 (info.mode == AUMODE_PLAY) ? "PLAY" 111 : (info.mode = AUMODE_RECORD) ? "RECORD" 112 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?")); 113 114 fprintf(stderr, "\n" 115 "[audio spec]\n" 116 "format : 0x%x\n" 117 "size : %u\n" 118 "", 119 this->spec.format, 120 this->spec.size); 121 /* *INDENT-ON* */ 122 #endif /* DEBUG_AUDIO */ 123 } 124 125 126 static void 127 NETBSDAUDIO_PlayDevice(_THIS) 128 { 129 struct SDL_PrivateAudioData *h = this->hidden; 130 int written; 131 132 /* Write the audio data */ 133 written = write(h->audio_fd, h->mixbuf, h->mixlen); 134 if (written == -1) { 135 /* Non recoverable error has occurred. It should be reported!!! */ 136 SDL_OpenedAudioDeviceDisconnected(this); 137 perror("audio"); 138 return; 139 } 140 141 #ifdef DEBUG_AUDIO 142 fprintf(stderr, "Wrote %d bytes of audio data\n", written); 143 #endif 144 } 145 146 static Uint8 * 147 NETBSDAUDIO_GetDeviceBuf(_THIS) 148 { 149 return (this->hidden->mixbuf); 150 } 151 152 153 static int 154 NETBSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen) 155 { 156 Uint8 *buffer = (Uint8 *) _buffer; 157 int br; 158 159 br = read(this->hidden->audio_fd, buffer, buflen); 160 if (br == -1) { 161 /* Non recoverable error has occurred. It should be reported!!! */ 162 perror("audio"); 163 return -1; 164 } 165 166 #ifdef DEBUG_AUDIO 167 fprintf(stderr, "Captured %d bytes of audio data\n", br); 168 #endif 169 return 0; 170 } 171 172 static void 173 NETBSDAUDIO_FlushCapture(_THIS) 174 { 175 audio_info_t info; 176 size_t remain; 177 Uint8 buf[512]; 178 179 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) { 180 return; /* oh well. */ 181 } 182 183 remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8)); 184 while (remain > 0) { 185 const size_t len = SDL_min(sizeof (buf), remain); 186 const int br = read(this->hidden->audio_fd, buf, len); 187 if (br <= 0) { 188 return; /* oh well. */ 189 } 190 remain -= br; 191 } 192 } 193 194 static void 195 NETBSDAUDIO_CloseDevice(_THIS) 196 { 197 if (this->hidden->audio_fd >= 0) { 198 close(this->hidden->audio_fd); 199 } 200 SDL_free(this->hidden->mixbuf); 201 SDL_free(this->hidden); 202 } 203 204 static int 205 NETBSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 206 { 207 SDL_AudioFormat format = 0; 208 audio_info_t info; 209 struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play; 210 211 /* We don't care what the devname is...we'll try to open anything. */ 212 /* ...but default to first name in the list... */ 213 if (devname == NULL) { 214 devname = SDL_GetAudioDeviceName(0, iscapture); 215 if (devname == NULL) { 216 return SDL_SetError("No such audio device"); 217 } 218 } 219 220 /* Initialize all variables that we clean on shutdown */ 221 this->hidden = (struct SDL_PrivateAudioData *) 222 SDL_malloc((sizeof *this->hidden)); 223 if (this->hidden == NULL) { 224 return SDL_OutOfMemory(); 225 } 226 SDL_zerop(this->hidden); 227 228 /* Open the audio device */ 229 this->hidden->audio_fd = open(devname, iscapture ? O_RDONLY : O_WRONLY); 230 if (this->hidden->audio_fd < 0) { 231 return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); 232 } 233 234 AUDIO_INITINFO(&info); 235 236 prinfo->encoding = AUDIO_ENCODING_NONE; 237 238 for (format = SDL_FirstAudioFormat(this->spec.format); format;) { 239 switch (format) { 240 case AUDIO_U8: 241 prinfo->encoding = AUDIO_ENCODING_ULINEAR; 242 prinfo->precision = 8; 243 break; 244 case AUDIO_S8: 245 prinfo->encoding = AUDIO_ENCODING_SLINEAR; 246 prinfo->precision = 8; 247 break; 248 case AUDIO_S16LSB: 249 prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE; 250 prinfo->precision = 16; 251 break; 252 case AUDIO_S16MSB: 253 prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE; 254 prinfo->precision = 16; 255 break; 256 case AUDIO_U16LSB: 257 prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE; 258 prinfo->precision = 16; 259 break; 260 case AUDIO_U16MSB: 261 prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE; 262 prinfo->precision = 16; 263 break; 264 case AUDIO_S32LSB: 265 prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE; 266 prinfo->precision = 32; 267 break; 268 case AUDIO_S32MSB: 269 prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE; 270 prinfo->precision = 32; 271 break; 272 } 273 if (prinfo->encoding != AUDIO_ENCODING_NONE) { 274 break; 275 } 276 format = SDL_NextAudioFormat(); 277 } 278 279 if (prinfo->encoding == AUDIO_ENCODING_NONE) { 280 return SDL_SetError("No supported encoding for 0x%x", this->spec.format); 281 } 282 283 this->spec.format = format; 284 285 /* Calculate spec parameters based on our chosen format */ 286 SDL_CalculateAudioSpec(&this->spec); 287 288 info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY; 289 info.blocksize = this->spec.size; 290 info.hiwat = 5; 291 info.lowat = 3; 292 prinfo->sample_rate = this->spec.freq; 293 prinfo->channels = this->spec.channels; 294 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info); 295 296 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info); 297 this->spec.freq = prinfo->sample_rate; 298 this->spec.channels = prinfo->channels; 299 300 if (!iscapture) { 301 /* Allocate mixing buffer */ 302 this->hidden->mixlen = this->spec.size; 303 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen); 304 if (this->hidden->mixbuf == NULL) { 305 return SDL_OutOfMemory(); 306 } 307 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size); 308 } 309 310 NETBSDAUDIO_Status(this); 311 312 /* We're ready to rock and roll. :-) */ 313 return 0; 314 } 315 316 static int 317 NETBSDAUDIO_Init(SDL_AudioDriverImpl * impl) 318 { 319 /* Set the function pointers */ 320 impl->DetectDevices = NETBSDAUDIO_DetectDevices; 321 impl->OpenDevice = NETBSDAUDIO_OpenDevice; 322 impl->PlayDevice = NETBSDAUDIO_PlayDevice; 323 impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf; 324 impl->CloseDevice = NETBSDAUDIO_CloseDevice; 325 impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice; 326 impl->FlushCapture = NETBSDAUDIO_FlushCapture; 327 328 impl->HasCaptureSupport = SDL_TRUE; 329 impl->AllowsArbitraryDeviceNames = 1; 330 331 return 1; /* this audio target is available. */ 332 } 333 334 335 AudioBootStrap NETBSDAUDIO_bootstrap = { 336 "netbsd", "NetBSD audio", NETBSDAUDIO_Init, 0 337 }; 338 339 #endif /* SDL_AUDIO_DRIVER_NETBSD */ 340 341 /* vi: set ts=4 sw=4 expandtab: */