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
NETBSDAUDIO_DetectDevices(void)49 NETBSDAUDIO_DetectDevices(void)
50 {
51 SDL_EnumUnixAudioDevices(0, NULL);
52 }
53
54
55 static void
NETBSDAUDIO_Status(_THIS)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
NETBSDAUDIO_PlayDevice(_THIS)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 *
NETBSDAUDIO_GetDeviceBuf(_THIS)147 NETBSDAUDIO_GetDeviceBuf(_THIS)
148 {
149 return (this->hidden->mixbuf);
150 }
151
152
153 static int
NETBSDAUDIO_CaptureFromDevice(_THIS,void * _buffer,int buflen)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
NETBSDAUDIO_FlushCapture(_THIS)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
NETBSDAUDIO_CloseDevice(_THIS)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
NETBSDAUDIO_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)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
NETBSDAUDIO_Init(SDL_AudioDriverImpl * impl)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: */
342