| @@ -1,18 +1,18 @@ | | | @@ -1,18 +1,18 @@ |
1 | $NetBSD: patch-audio_out_ao__netbsd.c,v 1.4 2020/04/08 12:17:02 nia Exp $ | | 1 | $NetBSD: patch-audio_out_ao__netbsd.c,v 1.5 2020/04/09 20:53:39 nia Exp $ |
2 | | | 2 | |
3 | NetBSD audio support. | | 3 | NetBSD audio support. |
4 | | | 4 | |
5 | --- audio/out/ao_netbsd.c.orig 2020-04-08 12:06:20.470592603 +0000 | | 5 | --- audio/out/ao_netbsd.c.orig 2020-04-09 20:51:23.971921857 +0000 |
6 | +++ audio/out/ao_netbsd.c | | 6 | +++ audio/out/ao_netbsd.c |
7 | @@ -0,0 +1,276 @@ | | 7 | @@ -0,0 +1,276 @@ |
8 | +/* | | 8 | +/* |
9 | + * Copyright (c) 2020 Nia Alarie <nia@NetBSD.org> | | 9 | + * Copyright (c) 2020 Nia Alarie <nia@NetBSD.org> |
10 | + * All rights reserved. | | 10 | + * All rights reserved. |
11 | + * | | 11 | + * |
12 | + * Redistribution and use in source and binary forms, with or without | | 12 | + * Redistribution and use in source and binary forms, with or without |
13 | + * modification, are permitted provided that the following conditions | | 13 | + * modification, are permitted provided that the following conditions |
14 | + * are met: | | 14 | + * are met: |
15 | + * | | 15 | + * |
16 | + * 1. Redistributions of source code must retain the above copyright | | 16 | + * 1. Redistributions of source code must retain the above copyright |
17 | + * notice, this list of conditions and the following disclaimer. | | 17 | + * notice, this list of conditions and the following disclaimer. |
18 | + * 2. Redistributions in binary form must reproduce the above copyright | | 18 | + * 2. Redistributions in binary form must reproduce the above copyright |
| @@ -36,73 +36,71 @@ NetBSD audio support. | | | @@ -36,73 +36,71 @@ NetBSD audio support. |
36 | +#include <unistd.h> | | 36 | +#include <unistd.h> |
37 | +#include <fcntl.h> | | 37 | +#include <fcntl.h> |
38 | +#include <errno.h> | | 38 | +#include <errno.h> |
39 | +#include <stdint.h> | | 39 | +#include <stdint.h> |
40 | +#include <string.h> | | 40 | +#include <string.h> |
41 | + | | 41 | + |
42 | +#include "options/m_option.h" | | 42 | +#include "options/m_option.h" |
43 | +#include "common/msg.h" | | 43 | +#include "common/msg.h" |
44 | + | | 44 | + |
45 | +#include "audio/format.h" | | 45 | +#include "audio/format.h" |
46 | +#include "ao.h" | | 46 | +#include "ao.h" |
47 | +#include "internal.h" | | 47 | +#include "internal.h" |
48 | + | | 48 | + |
49 | +#ifndef NETBSD_MAX_DEVS | | 49 | +#ifndef NETBSD_MAX_CHANNELS |
50 | +#define NETBSD_MAX_DEVS (8) | | 50 | +#define NETBSD_MAX_CHANNELS (12) |
51 | +#endif | | 51 | +#endif |
52 | + | | 52 | + |
53 | +#ifndef NETBSD_BUF_SIZE | | 53 | +#ifndef NETBSD_MAX_DEVS |
54 | +#define NETBSD_BUF_SIZE (1024) | | 54 | +#define NETBSD_MAX_DEVS (8) |
55 | +#endif | | 55 | +#endif |
56 | + | | 56 | + |
57 | +struct priv { | | 57 | +struct priv { |
58 | + int fd; | | 58 | + int fd; |
59 | + uint64_t total_blocks; /* audio blocks output */ | | | |
60 | + uint64_t total_bytes; /* bytes sent to the queue */ | | | |
61 | +}; | | 59 | +}; |
62 | + | | 60 | + |
63 | +static int init(struct ao *ao) | | 61 | +static int init(struct ao *ao) |
64 | +{ | | 62 | +{ |
65 | + char device[16] = "/dev/audio"; | | 63 | + char device[16] = "/dev/audio"; |
66 | + struct priv *p = ao->priv; | | 64 | + struct priv *p = ao->priv; |
67 | + struct audio_info info; | | 65 | + struct audio_info info; |
68 | + struct audio_info hw_info; | | 66 | + struct audio_info hw_info; |
69 | + struct audio_prinfo *pinfo; | | 67 | + struct audio_prinfo *pinfo; |
70 | + struct mp_chmap_sel sel = {0}; | | 68 | + struct mp_chmap_sel sel = {0}; |
71 | + | | 69 | + |
72 | + AUDIO_INITINFO(&info); | | 70 | + AUDIO_INITINFO(&info); |
73 | + AUDIO_INITINFO(&hw_info); | | 71 | + AUDIO_INITINFO(&hw_info); |
74 | + pinfo = &info.play; | | 72 | + pinfo = &info.play; |
75 | + | | 73 | + |
76 | + if (ao->device != NULL) { | | 74 | + if (ao->device != NULL) { |
77 | + (void)snprintf(device, sizeof(device), "/dev/audio%s", | | 75 | + (void)snprintf(device, sizeof(device), "/dev/audio%s", |
78 | + ao->device + sizeof("netbsd/") - 1); | | 76 | + ao->device + sizeof("netbsd/") - 1); |
79 | + } | | 77 | + } |
80 | + | | 78 | + |
81 | + MP_ERR(ao, "Opening device %s\n", device); | | 79 | + MP_ERR(ao, "Opening device %s\n", device); |
82 | + if ((p->fd = open(device, O_WRONLY)) == -1) { | | 80 | + if ((p->fd = open(device, O_WRONLY | O_NONBLOCK)) == -1) { |
83 | + MP_ERR(ao, "Can't open audio device %s: %s\n", | | 81 | + MP_ERR(ao, "Can't open audio device %s: %s\n", |
84 | + device, mp_strerror(errno)); | | 82 | + device, mp_strerror(errno)); |
85 | + goto fail; | | 83 | + goto fail; |
86 | + } | | 84 | + } |
87 | + | | 85 | + |
88 | + if (ioctl(p->fd, AUDIO_GETFORMAT, &hw_info) == -1) { | | 86 | + if (ioctl(p->fd, AUDIO_GETFORMAT, &hw_info) == -1) { |
89 | + MP_ERR(ao, "AUDIO_GETFORMAT failed: %s\n", mp_strerror(errno)); | | 87 | + MP_ERR(ao, "AUDIO_GETFORMAT failed: %s\n", mp_strerror(errno)); |
90 | + goto fail; | | 88 | + goto fail; |
91 | + } | | 89 | + } |
92 | + | | 90 | + |
93 | + info.mode = AUMODE_PLAY; | | 91 | + info.mode = AUMODE_PLAY; |
94 | + | | 92 | + |
95 | + for (int n = 1; n <= hw_info.play.channels; n++) { | | 93 | + for (int n = 1; n <= NETBSD_MAX_CHANNELS; n++) { |
96 | + struct mp_chmap map; | | 94 | + struct mp_chmap map; |
97 | + | | 95 | + |
98 | + mp_chmap_from_channels(&map, n); | | 96 | + mp_chmap_from_channels(&map, n); |
99 | + mp_chmap_sel_add_map(&sel, &map); | | 97 | + mp_chmap_sel_add_map(&sel, &map); |
100 | + } | | 98 | + } |
101 | + | | 99 | + |
102 | + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { | | 100 | + if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) { |
103 | + MP_ERR(ao, "Failed to select a valid channel map\n"); | | 101 | + MP_ERR(ao, "Failed to select a valid channel map\n"); |
104 | + goto fail; | | 102 | + goto fail; |
105 | + } | | 103 | + } |
106 | + | | 104 | + |
107 | + pinfo->channels = ao->channels.num; | | 105 | + pinfo->channels = ao->channels.num; |
108 | + pinfo->sample_rate = ao->samplerate = hw_info.play.sample_rate; | | 106 | + pinfo->sample_rate = ao->samplerate = hw_info.play.sample_rate; |
| @@ -117,80 +115,91 @@ NetBSD audio support. | | | @@ -117,80 +115,91 @@ NetBSD audio support. |
117 | + pinfo->encoding = AUDIO_ENCODING_SLINEAR; | | 115 | + pinfo->encoding = AUDIO_ENCODING_SLINEAR; |
118 | + break; | | 116 | + break; |
119 | + default: | | 117 | + default: |
120 | + pinfo->precision = 32; | | 118 | + pinfo->precision = 32; |
121 | + pinfo->encoding = AUDIO_ENCODING_SLINEAR; | | 119 | + pinfo->encoding = AUDIO_ENCODING_SLINEAR; |
122 | + ao->format = AF_FORMAT_S32; | | 120 | + ao->format = AF_FORMAT_S32; |
123 | + break; | | 121 | + break; |
124 | + } | | 122 | + } |
125 | + | | 123 | + |
126 | + if (ioctl(p->fd, AUDIO_SETINFO, &info) == -1) { | | 124 | + if (ioctl(p->fd, AUDIO_SETINFO, &info) == -1) { |
127 | + MP_ERR(ao, "AUDIO_SETINFO failed: %s\n", mp_strerror(errno)); | | 125 | + MP_ERR(ao, "AUDIO_SETINFO failed: %s\n", mp_strerror(errno)); |
128 | + goto fail; | | 126 | + goto fail; |
129 | + } | | 127 | + } |
| | | 128 | + |
| | | 129 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { |
| | | 130 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); |
| | | 131 | + goto fail; |
| | | 132 | + } |
| | | 133 | + |
| | | 134 | + ao->period_size = info.blocksize / (pinfo->precision / 8) / pinfo->channels; |
| | | 135 | + ao->device_buffer = info.play.buffer_size / (pinfo->precision / 8) / pinfo->channels; |
| | | 136 | + |
130 | + return 0; | | 137 | + return 0; |
131 | + | | 138 | + |
132 | +fail: | | 139 | +fail: |
133 | + if (p->fd != -1) { | | 140 | + if (p->fd != -1) { |
134 | + close(p->fd); | | 141 | + close(p->fd); |
135 | + } | | 142 | + } |
136 | + return -1; | | 143 | + return -1; |
137 | +} | | 144 | +} |
138 | + | | 145 | + |
139 | +static void uninit(struct ao *ao) | | 146 | +static void uninit(struct ao *ao) |
140 | +{ | | 147 | +{ |
141 | + struct priv *p = ao->priv; | | 148 | + struct priv *p = ao->priv; |
142 | + | | 149 | + |
143 | + (void)ioctl(p->fd, AUDIO_FLUSH, NULL); | | 150 | + (void)ioctl(p->fd, AUDIO_FLUSH, NULL); |
144 | + (void)close(p->fd); | | 151 | + (void)close(p->fd); |
145 | + p->fd = -1; | | 152 | + p->fd = -1; |
146 | +} | | 153 | +} |
147 | + | | 154 | + |
148 | +static void reset(struct ao *ao) | | 155 | +static void reset(struct ao *ao) |
149 | +{ | | 156 | +{ |
150 | + struct priv *p = ao->priv; | | 157 | + struct priv *p = ao->priv; |
151 | + struct audio_info info; | | 158 | + struct audio_info info; |
152 | + struct audio_offset offset; | | | |
153 | + | | 159 | + |
154 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { | | 160 | + if (ioctl(p->fd, AUDIO_GETBUFINFO, &info) == -1) { |
155 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); | | 161 | + MP_ERR(ao, "AUDIO_GETBUFINFO failed: %s\n", mp_strerror(errno)); |
156 | + return; | | 162 | + return; |
157 | + } | | 163 | + } |
158 | + | | 164 | + |
159 | + (void)ioctl(p->fd, AUDIO_FLUSH, NULL); | | 165 | + (void)ioctl(p->fd, AUDIO_FLUSH, NULL); |
160 | + (void)ioctl(p->fd, AUDIO_GETOOFFS, &offset); /* reset deltablks */ | | | |
161 | + p->total_blocks = p->total_bytes / info.blocksize; | | | |
162 | +} | | 166 | +} |
163 | + | | 167 | + |
164 | +static void drain(struct ao *ao) | | 168 | +static void drain(struct ao *ao) |
165 | +{ | | 169 | +{ |
166 | + struct priv *p = ao->priv; | | 170 | + struct priv *p = ao->priv; |
167 | + struct audio_info info; | | 171 | + struct audio_info info; |
168 | + struct audio_offset offset; | | | |
169 | + | | 172 | + |
170 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { | | 173 | + if (ioctl(p->fd, AUDIO_GETBUFINFO, &info) == -1) { |
171 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); | | 174 | + MP_ERR(ao, "AUDIO_GETBUFINFO failed: %s\n", mp_strerror(errno)); |
172 | + return; | | 175 | + return; |
173 | + } | | 176 | + } |
174 | + | | 177 | + |
175 | + (void)ioctl(p->fd, AUDIO_DRAIN, NULL); | | 178 | + (void)ioctl(p->fd, AUDIO_DRAIN, NULL); |
176 | + (void)ioctl(p->fd, AUDIO_FLUSH, NULL); | | | |
177 | + (void)ioctl(p->fd, AUDIO_GETOOFFS, &offset); /* reset deltablks */ | | | |
178 | + p->total_blocks = p->total_bytes / info.blocksize; | | | |
179 | +} | | 179 | +} |
180 | + | | 180 | + |
181 | +static int get_space(struct ao *ao) | | 181 | +static int get_space(struct ao *ao) |
182 | +{ | | 182 | +{ |
183 | + return NETBSD_BUF_SIZE; | | 183 | + struct priv *p = ao->priv; |
| | | 184 | + struct audio_info info; |
| | | 185 | + unsigned int nblk; |
| | | 186 | + |
| | | 187 | + if (ioctl(p->fd, AUDIO_GETBUFINFO, &info) == -1) { |
| | | 188 | + MP_ERR(ao, "AUDIO_GETBUFINFO failed: %s\n", mp_strerror(errno)); |
| | | 189 | + return 0; |
| | | 190 | + } |
| | | 191 | + nblk = info.hiwat - (info.play.seek / info.blocksize); |
| | | 192 | + return nblk * ao->period_size; |
184 | +} | | 193 | +} |
185 | + | | 194 | + |
186 | +static void audio_pause(struct ao *ao) | | 195 | +static void audio_pause(struct ao *ao) |
187 | +{ | | 196 | +{ |
188 | + struct priv *p = ao->priv; | | 197 | + struct priv *p = ao->priv; |
189 | + struct audio_info info; | | 198 | + struct audio_info info; |
190 | + | | 199 | + |
191 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { | | 200 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { |
192 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); | | 201 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); |
193 | + return; | | 202 | + return; |
194 | + } | | 203 | + } |
195 | + info.play.pause = 1; | | 204 | + info.play.pause = 1; |
196 | + (void)ioctl(p->fd, AUDIO_SETINFO, &info); | | 205 | + (void)ioctl(p->fd, AUDIO_SETINFO, &info); |
| @@ -204,57 +213,48 @@ NetBSD audio support. | | | @@ -204,57 +213,48 @@ NetBSD audio support. |
204 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { | | 213 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { |
205 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); | | 214 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); |
206 | + return; | | 215 | + return; |
207 | + } | | 216 | + } |
208 | + info.play.pause = 0; | | 217 | + info.play.pause = 0; |
209 | + (void)ioctl(p->fd, AUDIO_SETINFO, &info); | | 218 | + (void)ioctl(p->fd, AUDIO_SETINFO, &info); |
210 | + reset(ao); | | 219 | + reset(ao); |
211 | +} | | 220 | +} |
212 | + | | 221 | + |
213 | +static double get_delay(struct ao *ao) | | 222 | +static double get_delay(struct ao *ao) |
214 | +{ | | 223 | +{ |
215 | + struct priv *p = ao->priv; | | 224 | + struct priv *p = ao->priv; |
216 | + struct audio_info info; | | 225 | + struct audio_info info; |
217 | + struct audio_offset offset; | | | |
218 | + uint64_t transfer_len; | | | |
219 | + | | 226 | + |
220 | + if (ioctl(p->fd, AUDIO_GETINFO, &info) == -1) { | | 227 | + if (ioctl(p->fd, AUDIO_GETBUFINFO, &info) == -1) { |
221 | + MP_ERR(ao, "AUDIO_GETINFO failed: %s\n", mp_strerror(errno)); | | 228 | + MP_ERR(ao, "AUDIO_GETBUFINFO failed: %s\n", mp_strerror(errno)); |
222 | + return 0; | | | |
223 | + } | | | |
224 | + if (ioctl(p->fd, AUDIO_GETOOFFS, &offset) == -1) { | | | |
225 | + MP_ERR(ao, "AUDIO_GETOOFFS failed: %s\n", mp_strerror(errno)); | | | |
226 | + return 0; | | 229 | + return 0; |
227 | + } | | 230 | + } |
228 | + p->total_blocks += offset.deltablks; | | 231 | + return (info.blocksize + info.play.seek) / (double)ao->bps; |
229 | + transfer_len = p->total_bytes - (p->total_blocks * info.blocksize); | | | |
230 | + return transfer_len / (double)ao->bps; | | | |
231 | +} | | 232 | +} |
232 | + | | 233 | + |
233 | +static int play(struct ao *ao, void **data, int samples, int flags) | | 234 | +static int play(struct ao *ao, void **data, int samples, int flags) |
234 | +{ | | 235 | +{ |
235 | + struct priv *p = ao->priv; | | 236 | + struct priv *p = ao->priv; |
236 | + int len = samples * ao->sstride; | | 237 | + int len = samples * ao->sstride; |
237 | + int ret; | | 238 | + int ret; |
238 | + | | 239 | + |
239 | + if (len == 0) | | 240 | + if (len == 0) |
240 | + return 0; | | 241 | + return 0; |
241 | + | | 242 | + |
242 | + errno = 0; | | 243 | + errno = 0; |
243 | + if ((ret = write(p->fd, data[0], len)) == -1) { | | 244 | + if ((ret = write(p->fd, data[0], len)) == -1) { |
244 | + MP_ERR(ao, "audio write failed: %s\n", mp_strerror(errno)); | | 245 | + MP_ERR(ao, "audio write failed: %s\n", mp_strerror(errno)); |
245 | + return 0; | | 246 | + return 0; |
246 | + } | | 247 | + } |
247 | + p->total_bytes += ret; | | | |
248 | + return ret / ao->sstride; | | 248 | + return ret / ao->sstride; |
249 | +} | | 249 | +} |
250 | + | | 250 | + |
251 | +static void list_devs(struct ao *ao, struct ao_device_list *list) | | 251 | +static void list_devs(struct ao *ao, struct ao_device_list *list) |
252 | +{ | | 252 | +{ |
253 | + char name[16]; | | 253 | + char name[16]; |
254 | + struct audio_device dev; | | 254 | + struct audio_device dev; |
255 | + int fd; | | 255 | + int fd; |
256 | + | | 256 | + |
257 | + for (int i = 0; 0 < NETBSD_MAX_DEVS; ++i) { | | 257 | + for (int i = 0; 0 < NETBSD_MAX_DEVS; ++i) { |
258 | + (void)snprintf(name, sizeof(name), "/dev/audio%d", i); | | 258 | + (void)snprintf(name, sizeof(name), "/dev/audio%d", i); |
259 | + fd = open(name, O_WRONLY); | | 259 | + fd = open(name, O_WRONLY); |
260 | + if (fd == -1 || ioctl(fd, AUDIO_GETDEV, &dev) == -1) { | | 260 | + if (fd == -1 || ioctl(fd, AUDIO_GETDEV, &dev) == -1) { |