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