| @@ -1,754 +1,767 @@ | | | @@ -1,754 +1,767 @@ |
1 | /* | | 1 | /* |
2 | * Copyright © 2019 Nia Alarie | | 2 | * Copyright © 2019 Nia Alarie |
3 | * | | 3 | * |
4 | * This program is made available under an ISC-style license. See the | | 4 | * This program is made available under an ISC-style license. See the |
5 | * accompanying file LICENSE for details. | | 5 | * accompanying file LICENSE for details. |
6 | */ | | 6 | */ |
7 | #include <sys/audioio.h> | | 7 | #include <sys/audioio.h> |
8 | #include <sys/ioctl.h> | | 8 | #include <sys/ioctl.h> |
9 | #include <fcntl.h> | | 9 | #include <fcntl.h> |
10 | #include <unistd.h> | | 10 | #include <unistd.h> |
11 | #include <pthread.h> | | 11 | #include <pthread.h> |
12 | #include <stdlib.h> | | 12 | #include <stdlib.h> |
13 | #include <stdio.h> | | 13 | #include <stdio.h> |
14 | #include <string.h> | | 14 | #include <string.h> |
15 | #include <math.h> | | 15 | #include <math.h> |
16 | #include "cubeb/cubeb.h" | | 16 | #include "cubeb/cubeb.h" |
17 | #include "cubeb-internal.h" | | 17 | #include "cubeb-internal.h" |
18 | | | 18 | |
19 | #define CUBEB_OLD_API /* seamonkey and older firefox */ | | 19 | #define CUBEB_OLD_API /* seamonkey and older firefox */ |
20 | | | 20 | |
21 | #define BYTES_TO_FRAMES(bytes, channels) \ | | 21 | #define BYTES_TO_FRAMES(bytes, channels) \ |
22 | (bytes / (channels * sizeof(int16_t))) | | 22 | (bytes / (channels * sizeof(int16_t))) |
23 | | | 23 | |
24 | #define FRAMES_TO_BYTES(frames, channels) \ | | 24 | #define FRAMES_TO_BYTES(frames, channels) \ |
25 | (frames * (channels * sizeof(int16_t))) | | 25 | (frames * (channels * sizeof(int16_t))) |
26 | | | 26 | |
27 | /* Default to 4 + 1 for the default device. */ | | 27 | /* Default to 4 + 1 for the default device. */ |
28 | #ifndef SUN_DEVICE_COUNT | | 28 | #ifndef SUN_DEVICE_COUNT |
29 | #define SUN_DEVICE_COUNT (5) | | 29 | #define SUN_DEVICE_COUNT (5) |
30 | #endif | | 30 | #endif |
31 | | | 31 | |
32 | /* Supported well by most hardware. */ | | 32 | /* Supported well by most hardware. */ |
33 | #ifndef SUN_PREFER_RATE | | 33 | #ifndef SUN_PREFER_RATE |
34 | #define SUN_PREFER_RATE (48000) | | 34 | #define SUN_PREFER_RATE (48000) |
35 | #endif | | 35 | #endif |
36 | | | 36 | |
37 | /* Standard acceptable minimum. */ | | 37 | /* Standard acceptable minimum. */ |
38 | #ifndef SUN_LATENCY_MS | | 38 | #ifndef SUN_LATENCY_MS |
39 | #define SUN_LATENCY_MS (40) | | 39 | #define SUN_LATENCY_MS (40) |
40 | #endif | | 40 | #endif |
41 | | | 41 | |
42 | #ifndef SUN_DEFAULT_DEVICE | | 42 | #ifndef SUN_DEFAULT_DEVICE |
43 | #define SUN_DEFAULT_DEVICE "/dev/audio" | | 43 | #define SUN_DEFAULT_DEVICE "/dev/audio" |
44 | #endif | | 44 | #endif |
45 | | | 45 | |
46 | #ifndef SUN_POLL_TIMEOUT | | 46 | #ifndef SUN_POLL_TIMEOUT |
47 | #define SUN_POLL_TIMEOUT (1000) | | 47 | #define SUN_POLL_TIMEOUT (1000) |
48 | #endif | | 48 | #endif |
49 | | | 49 | |
50 | #ifndef SUN_BUFFER_FRAMES | | 50 | #ifndef SUN_BUFFER_FRAMES |
51 | #define SUN_BUFFER_FRAMES (32) | | 51 | #define SUN_BUFFER_FRAMES (32) |
52 | #endif | | 52 | #endif |
53 | | | 53 | |
54 | /* | | 54 | /* |
55 | * Supported on NetBSD regardless of hardware. | | 55 | * Supported on NetBSD regardless of hardware. |
56 | */ | | 56 | */ |
57 | | | 57 | |
58 | #ifndef SUN_MAX_CHANNELS | | 58 | #ifndef SUN_MAX_CHANNELS |
59 | # ifdef __NetBSD__ | | 59 | # ifdef __NetBSD__ |
60 | # define SUN_MAX_CHANNELS (12) | | 60 | # define SUN_MAX_CHANNELS (12) |
61 | # else | | 61 | # else |
62 | # define SUN_MAX_CHANNELS (2) | | 62 | # define SUN_MAX_CHANNELS (2) |
63 | # endif | | 63 | # endif |
64 | #endif | | 64 | #endif |
65 | | | 65 | |
66 | #ifndef SUN_MIN_RATE | | 66 | #ifndef SUN_MIN_RATE |
67 | #define SUN_MIN_RATE (1000) | | 67 | #define SUN_MIN_RATE (1000) |
68 | #endif | | 68 | #endif |
69 | | | 69 | |
70 | #ifndef SUN_MAX_RATE | | 70 | #ifndef SUN_MAX_RATE |
71 | #define SUN_MAX_RATE (192000) | | 71 | #define SUN_MAX_RATE (192000) |
72 | #endif | | 72 | #endif |
73 | | | 73 | |
74 | static struct cubeb_ops const sun_ops; | | 74 | static struct cubeb_ops const sun_ops; |
75 | | | 75 | |
76 | struct cubeb { | | 76 | struct cubeb { |
77 | struct cubeb_ops const * ops; | | 77 | struct cubeb_ops const * ops; |
78 | }; | | 78 | }; |
79 | | | 79 | |
80 | struct cubeb_stream { | | 80 | struct cubeb_stream { |
81 | struct cubeb * context; | | 81 | struct cubeb * context; |
82 | void * user_ptr; | | 82 | void * user_ptr; |
83 | pthread_t thread; | | 83 | pthread_t thread; |
84 | pthread_mutex_t mutex; /* protects running, volume, frames_written */ | | 84 | pthread_mutex_t mutex; /* protects running, volume, frames_written */ |
85 | int floating; | | 85 | int floating; |
86 | int running; | | 86 | int running; |
87 | int play_fd; | | 87 | int play_fd; |
88 | int record_fd; | | 88 | int record_fd; |
89 | float volume; | | 89 | float volume; |
90 | struct audio_info p_info; /* info for the play fd */ | | 90 | struct audio_info p_info; /* info for the play fd */ |
91 | struct audio_info r_info; /* info for the record fd */ | | 91 | struct audio_info r_info; /* info for the record fd */ |
92 | cubeb_data_callback data_cb; | | 92 | cubeb_data_callback data_cb; |
93 | cubeb_state_callback state_cb; | | 93 | cubeb_state_callback state_cb; |
94 | int16_t * play_buf; | | 94 | int16_t * play_buf; |
95 | int16_t * record_buf; | | 95 | int16_t * record_buf; |
96 | float * f_play_buf; | | 96 | float * f_play_buf; |
97 | float * f_record_buf; | | 97 | float * f_record_buf; |
98 | char input_name[32]; | | 98 | char input_name[32]; |
99 | char output_name[32]; | | 99 | char output_name[32]; |
100 | uint64_t frames_written; | | 100 | uint64_t frames_written; |
| | | 101 | uint64_t blocks_written; |
101 | }; | | 102 | }; |
102 | | | 103 | |
103 | int | | 104 | int |
104 | sun_init(cubeb ** context, char const * context_name) | | 105 | sun_init(cubeb ** context, char const * context_name) |
105 | { | | 106 | { |
106 | cubeb * c; | | 107 | cubeb * c; |
107 | | | 108 | |
108 | (void)context_name; | | 109 | (void)context_name; |
109 | if ((c = calloc(1, sizeof(cubeb))) == NULL) { | | 110 | if ((c = calloc(1, sizeof(cubeb))) == NULL) { |
110 | return CUBEB_ERROR; | | 111 | return CUBEB_ERROR; |
111 | } | | 112 | } |
112 | c->ops = &sun_ops; | | 113 | c->ops = &sun_ops; |
113 | *context = c; | | 114 | *context = c; |
114 | return CUBEB_OK; | | 115 | return CUBEB_OK; |
115 | } | | 116 | } |
116 | | | 117 | |
117 | static void | | 118 | static void |
118 | sun_destroy(cubeb * context) | | 119 | sun_destroy(cubeb * context) |
119 | { | | 120 | { |
120 | free(context); | | 121 | free(context); |
121 | } | | 122 | } |
122 | | | 123 | |
123 | static char const * | | 124 | static char const * |
124 | sun_get_backend_id(cubeb * context) | | 125 | sun_get_backend_id(cubeb * context) |
125 | { | | 126 | { |
126 | return "sun"; | | 127 | return "sun"; |
127 | } | | 128 | } |
128 | | | 129 | |
129 | static int | | 130 | static int |
130 | sun_get_preferred_sample_rate(cubeb * context, uint32_t * rate) | | 131 | sun_get_preferred_sample_rate(cubeb * context, uint32_t * rate) |
131 | { | | 132 | { |
132 | (void)context; | | 133 | (void)context; |
133 | | | 134 | |
134 | *rate = SUN_PREFER_RATE; | | 135 | *rate = SUN_PREFER_RATE; |
135 | return CUBEB_OK; | | 136 | return CUBEB_OK; |
136 | } | | 137 | } |
137 | | | 138 | |
138 | static int | | 139 | static int |
139 | sun_get_max_channel_count(cubeb * context, uint32_t * max_channels) | | 140 | sun_get_max_channel_count(cubeb * context, uint32_t * max_channels) |
140 | { | | 141 | { |
141 | (void)context; | | 142 | (void)context; |
142 | | | 143 | |
143 | *max_channels = SUN_MAX_CHANNELS; | | 144 | *max_channels = SUN_MAX_CHANNELS; |
144 | return CUBEB_OK; | | 145 | return CUBEB_OK; |
145 | } | | 146 | } |
146 | | | 147 | |
147 | static int | | 148 | static int |
148 | sun_get_min_latency(cubeb * context, cubeb_stream_params params, | | 149 | sun_get_min_latency(cubeb * context, cubeb_stream_params params, |
149 | uint32_t * latency_frames) | | 150 | uint32_t * latency_frames) |
150 | { | | 151 | { |
151 | (void)context; | | 152 | (void)context; |
152 | | | 153 | |
153 | *latency_frames = SUN_LATENCY_MS * params.rate / 1000; | | 154 | *latency_frames = SUN_LATENCY_MS * params.rate / 1000; |
154 | return CUBEB_OK; | | 155 | return CUBEB_OK; |
155 | } | | 156 | } |
156 | | | 157 | |
157 | static int | | 158 | static int |
158 | sun_get_hwinfo(const char * device, struct audio_info * format, | | 159 | sun_get_hwinfo(const char * device, struct audio_info * format, |
159 | int * props, struct audio_device * dev) | | 160 | int * props, struct audio_device * dev) |
160 | { | | 161 | { |
161 | int fd = -1; | | 162 | int fd = -1; |
162 | | | 163 | |
163 | if ((fd = open(device, O_RDONLY)) == -1) { | | 164 | if ((fd = open(device, O_RDONLY)) == -1) { |
164 | goto error; | | 165 | goto error; |
165 | } | | 166 | } |
166 | #ifdef AUDIO_GETFORMAT | | 167 | #ifdef AUDIO_GETFORMAT |
167 | if (ioctl(fd, AUDIO_GETFORMAT, format) != 0) { | | 168 | if (ioctl(fd, AUDIO_GETFORMAT, format) != 0) { |
168 | goto error; | | 169 | goto error; |
169 | } | | 170 | } |
170 | #endif | | 171 | #endif |
171 | #ifdef AUDIO_GETPROPS | | 172 | #ifdef AUDIO_GETPROPS |
172 | if (ioctl(fd, AUDIO_GETPROPS, props) != 0) { | | 173 | if (ioctl(fd, AUDIO_GETPROPS, props) != 0) { |
173 | goto error; | | 174 | goto error; |
174 | } | | 175 | } |
175 | #endif | | 176 | #endif |
176 | if (ioctl(fd, AUDIO_GETDEV, dev) != 0) { | | 177 | if (ioctl(fd, AUDIO_GETDEV, dev) != 0) { |
177 | goto error; | | 178 | goto error; |
178 | } | | 179 | } |
179 | close(fd); | | 180 | close(fd); |
180 | return CUBEB_OK; | | 181 | return CUBEB_OK; |
181 | error: | | 182 | error: |
182 | if (fd != -1) { | | 183 | if (fd != -1) { |
183 | close(fd); | | 184 | close(fd); |
184 | } | | 185 | } |
185 | return CUBEB_ERROR; | | 186 | return CUBEB_ERROR; |
186 | } | | 187 | } |
187 | | | 188 | |
188 | /* | | 189 | /* |
189 | * XXX: PR kern/54264 | | 190 | * XXX: PR kern/54264 |
190 | */ | | 191 | */ |
191 | static int | | 192 | static int |
192 | sun_prinfo_verify_sanity(struct audio_prinfo * prinfo) | | 193 | sun_prinfo_verify_sanity(struct audio_prinfo * prinfo) |
193 | { | | 194 | { |
194 | return prinfo->precision >= 8 && prinfo->precision <= 32 && | | 195 | return prinfo->precision >= 8 && prinfo->precision <= 32 && |
195 | prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS && | | 196 | prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS && |
196 | prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE; | | 197 | prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE; |
197 | } | | 198 | } |
198 | | | 199 | |
199 | #ifndef CUBEB_OLD_API | | 200 | #ifndef CUBEB_OLD_API |
200 | static int | | 201 | static int |
201 | sun_enumerate_devices(cubeb * context, cubeb_device_type type, | | 202 | sun_enumerate_devices(cubeb * context, cubeb_device_type type, |
202 | cubeb_device_collection * collection) | | 203 | cubeb_device_collection * collection) |
203 | { | | 204 | { |
204 | unsigned i; | | 205 | unsigned i; |
205 | cubeb_device_info device = {0}; | | 206 | cubeb_device_info device = {0}; |
206 | char dev[16] = SUN_DEFAULT_DEVICE; | | 207 | char dev[16] = SUN_DEFAULT_DEVICE; |
207 | char dev_friendly[64]; | | 208 | char dev_friendly[64]; |
208 | struct audio_info hwfmt; | | 209 | struct audio_info hwfmt; |
209 | struct audio_device hwname; | | 210 | struct audio_device hwname; |
210 | struct audio_prinfo *prinfo = NULL; | | 211 | struct audio_prinfo *prinfo = NULL; |
211 | int hwprops; | | 212 | int hwprops; |
212 | | | 213 | |
213 | collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info)); | | 214 | collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info)); |
214 | if (collection->device == NULL) { | | 215 | if (collection->device == NULL) { |
215 | return CUBEB_ERROR; | | 216 | return CUBEB_ERROR; |
216 | } | | 217 | } |
217 | collection->count = 0; | | 218 | collection->count = 0; |
218 | | | 219 | |
219 | for (i = 0; i < SUN_DEVICE_COUNT; ++i) { | | 220 | for (i = 0; i < SUN_DEVICE_COUNT; ++i) { |
220 | if (i > 0) { | | 221 | if (i > 0) { |
221 | (void)snprintf(dev, sizeof(dev), "/dev/audio%u", i - 1); | | 222 | (void)snprintf(dev, sizeof(dev), "/dev/audio%u", i - 1); |
222 | } | | 223 | } |
223 | if (sun_get_hwinfo(dev, &hwfmt, &hwprops, &hwname) != CUBEB_OK) { | | 224 | if (sun_get_hwinfo(dev, &hwfmt, &hwprops, &hwname) != CUBEB_OK) { |
224 | continue; | | 225 | continue; |
225 | } | | 226 | } |
226 | #ifdef AUDIO_GETPROPS | | 227 | #ifdef AUDIO_GETPROPS |
227 | device.type = 0; | | 228 | device.type = 0; |
228 | if ((hwprops & AUDIO_PROP_CAPTURE) != 0 && | | 229 | if ((hwprops & AUDIO_PROP_CAPTURE) != 0 && |
229 | sun_prinfo_verify_sanity(&hwfmt.record)) { | | 230 | sun_prinfo_verify_sanity(&hwfmt.record)) { |
230 | /* the device supports recording, probably */ | | 231 | /* the device supports recording, probably */ |
231 | device.type |= CUBEB_DEVICE_TYPE_INPUT; | | 232 | device.type |= CUBEB_DEVICE_TYPE_INPUT; |
232 | } | | 233 | } |
233 | if ((hwprops & AUDIO_PROP_PLAYBACK) != 0 && | | 234 | if ((hwprops & AUDIO_PROP_PLAYBACK) != 0 && |
234 | sun_prinfo_verify_sanity(&hwfmt.play)) { | | 235 | sun_prinfo_verify_sanity(&hwfmt.play)) { |
235 | /* the device supports playback, probably */ | | 236 | /* the device supports playback, probably */ |
236 | device.type |= CUBEB_DEVICE_TYPE_OUTPUT; | | 237 | device.type |= CUBEB_DEVICE_TYPE_OUTPUT; |
237 | } | | 238 | } |
238 | switch (device.type) { | | 239 | switch (device.type) { |
239 | case 0: | | 240 | case 0: |
240 | /* device doesn't do input or output, aliens probably involved */ | | 241 | /* device doesn't do input or output, aliens probably involved */ |
241 | continue; | | 242 | continue; |
242 | case CUBEB_DEVICE_TYPE_INPUT: | | 243 | case CUBEB_DEVICE_TYPE_INPUT: |
243 | if ((type & CUBEB_DEVICE_TYPE_INPUT) == 0) { | | 244 | if ((type & CUBEB_DEVICE_TYPE_INPUT) == 0) { |
244 | /* this device is input only, not scanning for those, skip it */ | | 245 | /* this device is input only, not scanning for those, skip it */ |
245 | continue; | | 246 | continue; |
246 | } | | 247 | } |
247 | break; | | 248 | break; |
248 | case CUBEB_DEVICE_TYPE_OUTPUT: | | 249 | case CUBEB_DEVICE_TYPE_OUTPUT: |
249 | if ((type & CUBEB_DEVICE_TYPE_OUTPUT) == 0) { | | 250 | if ((type & CUBEB_DEVICE_TYPE_OUTPUT) == 0) { |
250 | /* this device is output only, not scanning for those, skip it */ | | 251 | /* this device is output only, not scanning for those, skip it */ |
251 | continue; | | 252 | continue; |
252 | } | | 253 | } |
253 | break; | | 254 | break; |
254 | } | | 255 | } |
255 | if ((type & CUBEB_DEVICE_TYPE_INPUT) != 0) { | | 256 | if ((type & CUBEB_DEVICE_TYPE_INPUT) != 0) { |
256 | prinfo = &hwfmt.record; | | 257 | prinfo = &hwfmt.record; |
257 | } | | 258 | } |
258 | if ((type & CUBEB_DEVICE_TYPE_OUTPUT) != 0) { | | 259 | if ((type & CUBEB_DEVICE_TYPE_OUTPUT) != 0) { |
259 | prinfo = &hwfmt.play; | | 260 | prinfo = &hwfmt.play; |
260 | } | | 261 | } |
261 | #endif | | 262 | #endif |
262 | if (i > 0) { | | 263 | if (i > 0) { |
263 | (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (%d)", | | 264 | (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (%d)", |
264 | hwname.name, hwname.version, hwname.config, i - 1); | | 265 | hwname.name, hwname.version, hwname.config, i - 1); |
265 | } else { | | 266 | } else { |
266 | (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (default)", | | 267 | (void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (default)", |
267 | hwname.name, hwname.version, hwname.config); | | 268 | hwname.name, hwname.version, hwname.config); |
268 | } | | 269 | } |
269 | device.devid = (void *)(uintptr_t)i; | | 270 | device.devid = (void *)(uintptr_t)i; |
270 | device.device_id = strdup(dev); | | 271 | device.device_id = strdup(dev); |
271 | device.friendly_name = strdup(dev_friendly); | | 272 | device.friendly_name = strdup(dev_friendly); |
272 | device.group_id = strdup(dev); | | 273 | device.group_id = strdup(dev); |
273 | device.vendor_name = strdup(hwname.name); | | 274 | device.vendor_name = strdup(hwname.name); |
274 | device.type = type; | | 275 | device.type = type; |
275 | device.state = CUBEB_DEVICE_STATE_ENABLED; | | 276 | device.state = CUBEB_DEVICE_STATE_ENABLED; |
276 | device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; | | 277 | device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; |
277 | #ifdef AUDIO_GETFORMAT | | 278 | #ifdef AUDIO_GETFORMAT |
278 | device.max_channels = prinfo->channels; | | 279 | device.max_channels = prinfo->channels; |
279 | device.default_rate = prinfo->sample_rate; | | 280 | device.default_rate = prinfo->sample_rate; |
280 | #else | | 281 | #else |
281 | device.max_channels = 2; | | 282 | device.max_channels = 2; |
282 | device.default_rate = SUN_PREFER_RATE; | | 283 | device.default_rate = SUN_PREFER_RATE; |
283 | #endif | | 284 | #endif |
284 | device.default_format = CUBEB_DEVICE_FMT_S16NE; | | 285 | device.default_format = CUBEB_DEVICE_FMT_S16NE; |
285 | device.format = CUBEB_DEVICE_FMT_S16NE; | | 286 | device.format = CUBEB_DEVICE_FMT_S16NE; |
286 | device.min_rate = SUN_MIN_RATE; | | 287 | device.min_rate = SUN_MIN_RATE; |
287 | device.max_rate = SUN_MAX_RATE; | | 288 | device.max_rate = SUN_MAX_RATE; |
288 | device.latency_lo = SUN_LATENCY_MS * SUN_MIN_RATE / 1000; | | 289 | device.latency_lo = SUN_LATENCY_MS * SUN_MIN_RATE / 1000; |
289 | device.latency_hi = SUN_LATENCY_MS * SUN_MAX_RATE / 1000; | | 290 | device.latency_hi = SUN_LATENCY_MS * SUN_MAX_RATE / 1000; |
290 | collection->device[collection->count++] = device; | | 291 | collection->device[collection->count++] = device; |
291 | } | | 292 | } |
292 | return CUBEB_OK; | | 293 | return CUBEB_OK; |
293 | } | | 294 | } |
294 | #endif | | 295 | #endif |
295 | | | 296 | |
296 | #ifndef CUBEB_OLD_API | | 297 | #ifndef CUBEB_OLD_API |
297 | static int | | 298 | static int |
298 | sun_device_collection_destroy(cubeb * context, | | 299 | sun_device_collection_destroy(cubeb * context, |
299 | cubeb_device_collection * collection) | | 300 | cubeb_device_collection * collection) |
300 | { | | 301 | { |
301 | unsigned i; | | 302 | unsigned i; |
302 | | | 303 | |
303 | for (i = 0; i < collection->count; ++i) { | | 304 | for (i = 0; i < collection->count; ++i) { |
304 | free((char *)collection->device[i].device_id); | | 305 | free((char *)collection->device[i].device_id); |
305 | free((char *)collection->device[i].friendly_name); | | 306 | free((char *)collection->device[i].friendly_name); |
306 | free((char *)collection->device[i].group_id); | | 307 | free((char *)collection->device[i].group_id); |
307 | free((char *)collection->device[i].vendor_name); | | 308 | free((char *)collection->device[i].vendor_name); |
308 | } | | 309 | } |
309 | free(collection->device); | | 310 | free(collection->device); |
310 | return CUBEB_OK; | | 311 | return CUBEB_OK; |
311 | } | | 312 | } |
312 | #endif | | 313 | #endif |
313 | | | 314 | |
314 | static int | | 315 | static int |
315 | sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, | | 316 | sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, |
316 | struct audio_info * info, struct audio_prinfo * prinfo) | | 317 | struct audio_info * info, struct audio_prinfo * prinfo) |
317 | { | | 318 | { |
318 | prinfo->channels = params->channels; | | 319 | prinfo->channels = params->channels; |
319 | prinfo->sample_rate = params->rate; | | 320 | prinfo->sample_rate = params->rate; |
320 | prinfo->precision = 16; | | 321 | prinfo->precision = 16; |
321 | #ifdef AUDIO_ENCODING_SLINEAR_LE | | 322 | #ifdef AUDIO_ENCODING_SLINEAR_LE |
322 | switch (params->format) { | | 323 | switch (params->format) { |
323 | case CUBEB_SAMPLE_S16LE: | | 324 | case CUBEB_SAMPLE_S16LE: |
324 | prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE; | | 325 | prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE; |
325 | break; | | 326 | break; |
326 | case CUBEB_SAMPLE_S16BE: | | 327 | case CUBEB_SAMPLE_S16BE: |
327 | prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE; | | 328 | prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE; |
328 | break; | | 329 | break; |
329 | case CUBEB_SAMPLE_FLOAT32NE: | | 330 | case CUBEB_SAMPLE_FLOAT32NE: |
330 | stream->floating = 1; | | 331 | stream->floating = 1; |
331 | prinfo->encoding = AUDIO_ENCODING_SLINEAR; | | 332 | prinfo->encoding = AUDIO_ENCODING_SLINEAR; |
332 | break; | | 333 | break; |
333 | default: | | 334 | default: |
334 | LOG("Unsupported format"); | | 335 | LOG("Unsupported format"); |
335 | return CUBEB_ERROR_INVALID_FORMAT; | | 336 | return CUBEB_ERROR_INVALID_FORMAT; |
336 | } | | 337 | } |
337 | #else | | 338 | #else |
338 | switch (params->format) { | | 339 | switch (params->format) { |
339 | case CUBEB_SAMPLE_S16NE: | | 340 | case CUBEB_SAMPLE_S16NE: |
340 | prinfo->encoding = AUDIO_ENCODING_LINEAR; | | 341 | prinfo->encoding = AUDIO_ENCODING_LINEAR; |
341 | break; | | 342 | break; |
342 | case CUBEB_SAMPLE_FLOAT32NE: | | 343 | case CUBEB_SAMPLE_FLOAT32NE: |
343 | stream->floating = 1; | | 344 | stream->floating = 1; |
344 | prinfo->encoding = AUDIO_ENCODING_LINEAR; | | 345 | prinfo->encoding = AUDIO_ENCODING_LINEAR; |
345 | break; | | 346 | break; |
346 | default: | | 347 | default: |
347 | LOG("Unsupported format"); | | 348 | LOG("Unsupported format"); |
348 | return CUBEB_ERROR_INVALID_FORMAT; | | 349 | return CUBEB_ERROR_INVALID_FORMAT; |
349 | } | | 350 | } |
350 | #endif | | 351 | #endif |
351 | if (ioctl(fd, AUDIO_SETINFO, info) == -1) { | | 352 | if (ioctl(fd, AUDIO_SETINFO, info) == -1) { |
352 | return CUBEB_ERROR; | | 353 | return CUBEB_ERROR; |
353 | } | | 354 | } |
354 | if (ioctl(fd, AUDIO_GETINFO, info) == -1) { | | 355 | if (ioctl(fd, AUDIO_GETINFO, info) == -1) { |
355 | return CUBEB_ERROR; | | 356 | return CUBEB_ERROR; |
356 | } | | 357 | } |
357 | return CUBEB_OK; | | 358 | return CUBEB_OK; |
358 | } | | 359 | } |
359 | | | 360 | |
360 | static int | | 361 | static int |
361 | sun_stream_stop(cubeb_stream * s) | | 362 | sun_stream_stop(cubeb_stream * s) |
362 | { | | 363 | { |
363 | pthread_mutex_lock(&s->mutex); | | 364 | pthread_mutex_lock(&s->mutex); |
364 | if (s->running) { | | 365 | if (s->running) { |
365 | s->running = 0; | | 366 | s->running = 0; |
366 | pthread_mutex_unlock(&s->mutex); | | 367 | pthread_mutex_unlock(&s->mutex); |
367 | pthread_join(s->thread, NULL); | | 368 | pthread_join(s->thread, NULL); |
368 | } else { | | 369 | } else { |
369 | pthread_mutex_unlock(&s->mutex); | | 370 | pthread_mutex_unlock(&s->mutex); |
370 | } | | 371 | } |
371 | return CUBEB_OK; | | 372 | return CUBEB_OK; |
372 | } | | 373 | } |
373 | | | 374 | |
374 | static void | | 375 | static void |
375 | sun_stream_destroy(cubeb_stream * s) | | 376 | sun_stream_destroy(cubeb_stream * s) |
376 | { | | 377 | { |
377 | pthread_mutex_destroy(&s->mutex); | | 378 | pthread_mutex_destroy(&s->mutex); |
378 | sun_stream_stop(s); | | 379 | sun_stream_stop(s); |
379 | if (s->play_fd != -1) { | | 380 | if (s->play_fd != -1) { |
380 | close(s->play_fd); | | 381 | close(s->play_fd); |
381 | } | | 382 | } |
382 | if (s->record_fd != -1) { | | 383 | if (s->record_fd != -1) { |
383 | close(s->record_fd); | | 384 | close(s->record_fd); |
384 | } | | 385 | } |
385 | free(s->f_play_buf); | | 386 | free(s->f_play_buf); |
386 | free(s->f_record_buf); | | 387 | free(s->f_record_buf); |
387 | free(s->play_buf); | | 388 | free(s->play_buf); |
388 | free(s->record_buf); | | 389 | free(s->record_buf); |
389 | free(s); | | 390 | free(s); |
390 | } | | 391 | } |
391 | | | 392 | |
392 | static void | | 393 | static void |
393 | sun_float_to_linear(float * in, int16_t * out, | | 394 | sun_float_to_linear(float * in, int16_t * out, |
394 | unsigned channels, long frames, float vol) | | 395 | unsigned channels, long frames, float vol) |
395 | { | | 396 | { |
396 | unsigned i, sample_count = frames * channels; | | 397 | unsigned i, sample_count = frames * channels; |
397 | float multiplier = vol * 0x8000; | | 398 | float multiplier = vol * 0x8000; |
398 | | | 399 | |
399 | for (i = 0; i < sample_count; ++i) { | | 400 | for (i = 0; i < sample_count; ++i) { |
400 | int32_t sample = lrintf(in[i] * multiplier); | | 401 | int32_t sample = lrintf(in[i] * multiplier); |
401 | if (sample < -0x8000) { | | 402 | if (sample < -0x8000) { |
402 | out[i] = -0x8000; | | 403 | out[i] = -0x8000; |
403 | } else if (sample > 0x7fff) { | | 404 | } else if (sample > 0x7fff) { |
404 | out[i] = 0x7fff; | | 405 | out[i] = 0x7fff; |
405 | } else { | | 406 | } else { |
406 | out[i] = sample; | | 407 | out[i] = sample; |
407 | } | | 408 | } |
408 | } | | 409 | } |
409 | } | | 410 | } |
410 | | | 411 | |
411 | static void | | 412 | static void |
412 | sun_linear_to_float(int16_t * in, float * out, | | 413 | sun_linear_to_float(int16_t * in, float * out, |
413 | unsigned channels, long frames) | | 414 | unsigned channels, long frames) |
414 | { | | 415 | { |
415 | unsigned i, sample_count = frames * channels; | | 416 | unsigned i, sample_count = frames * channels; |
416 | | | 417 | |
417 | for (i = 0; i < sample_count; ++i) { | | 418 | for (i = 0; i < sample_count; ++i) { |
418 | out[i] = (1.0 / 0x8000) * in[i]; | | 419 | out[i] = (1.0 / 0x8000) * in[i]; |
419 | } | | 420 | } |
420 | } | | 421 | } |
421 | | | 422 | |
422 | static void | | 423 | static void |
423 | sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol) | | 424 | sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol) |
424 | { | | 425 | { |
425 | unsigned i, sample_count = frames * channels; | | 426 | unsigned i, sample_count = frames * channels; |
426 | int32_t multiplier = vol * 0x8000; | | 427 | int32_t multiplier = vol * 0x8000; |
427 | | | 428 | |
428 | for (i = 0; i < sample_count; ++i) { | | 429 | for (i = 0; i < sample_count; ++i) { |
429 | buf[i] = (buf[i] * multiplier) >> 15; | | 430 | buf[i] = (buf[i] * multiplier) >> 15; |
430 | } | | 431 | } |
431 | } | | 432 | } |
432 | | | 433 | |
433 | static void * | | 434 | static void * |
434 | sun_io_routine(void * arg) | | 435 | sun_io_routine(void * arg) |
435 | { | | 436 | { |
436 | cubeb_stream *s = arg; | | 437 | cubeb_stream *s = arg; |
437 | cubeb_state state = CUBEB_STATE_STARTED; | | 438 | cubeb_state state = CUBEB_STATE_STARTED; |
438 | size_t to_read = 0; | | 439 | size_t to_read = 0; |
439 | long to_write = 0; | | 440 | long to_write = 0; |
440 | size_t write_ofs = 0; | | 441 | size_t write_ofs = 0; |
441 | size_t read_ofs = 0; | | 442 | size_t read_ofs = 0; |
442 | int drain = 0; | | 443 | int drain = 0; |
443 | | | 444 | |
444 | s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED); | | 445 | s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED); |
445 | while (state != CUBEB_STATE_ERROR) { | | 446 | while (state != CUBEB_STATE_ERROR) { |
446 | pthread_mutex_lock(&s->mutex); | | 447 | pthread_mutex_lock(&s->mutex); |
447 | if (!s->running) { | | 448 | if (!s->running) { |
448 | pthread_mutex_unlock(&s->mutex); | | 449 | pthread_mutex_unlock(&s->mutex); |
449 | state = CUBEB_STATE_STOPPED; | | 450 | state = CUBEB_STATE_STOPPED; |
450 | break; | | 451 | break; |
451 | } | | 452 | } |
452 | pthread_mutex_unlock(&s->mutex); | | 453 | pthread_mutex_unlock(&s->mutex); |
453 | if (s->floating) { | | 454 | if (s->floating) { |
454 | if (s->record_fd != -1) { | | 455 | if (s->record_fd != -1) { |
455 | sun_linear_to_float(s->record_buf, s->f_record_buf, | | 456 | sun_linear_to_float(s->record_buf, s->f_record_buf, |
456 | s->r_info.record.channels, SUN_BUFFER_FRAMES); | | 457 | s->r_info.record.channels, SUN_BUFFER_FRAMES); |
457 | } | | 458 | } |
458 | to_write = s->data_cb(s, s->user_ptr, | | 459 | to_write = s->data_cb(s, s->user_ptr, |
459 | s->f_record_buf, s->f_play_buf, SUN_BUFFER_FRAMES); | | 460 | s->f_record_buf, s->f_play_buf, SUN_BUFFER_FRAMES); |
460 | if (to_write == CUBEB_ERROR) { | | 461 | if (to_write == CUBEB_ERROR) { |
461 | state = CUBEB_STATE_ERROR; | | 462 | state = CUBEB_STATE_ERROR; |
462 | break; | | 463 | break; |
463 | } | | 464 | } |
464 | if (s->play_fd != -1) { | | 465 | if (s->play_fd != -1) { |
465 | pthread_mutex_lock(&s->mutex); | | 466 | pthread_mutex_lock(&s->mutex); |
466 | sun_float_to_linear(s->f_play_buf, s->play_buf, | | 467 | sun_float_to_linear(s->f_play_buf, s->play_buf, |
467 | s->p_info.play.channels, to_write, s->volume); | | 468 | s->p_info.play.channels, to_write, s->volume); |
468 | pthread_mutex_unlock(&s->mutex); | | 469 | pthread_mutex_unlock(&s->mutex); |
469 | } | | 470 | } |
470 | } else { | | 471 | } else { |
471 | to_write = s->data_cb(s, s->user_ptr, | | 472 | to_write = s->data_cb(s, s->user_ptr, |
472 | s->record_buf, s->play_buf, SUN_BUFFER_FRAMES); | | 473 | s->record_buf, s->play_buf, SUN_BUFFER_FRAMES); |
473 | if (to_write == CUBEB_ERROR) { | | 474 | if (to_write == CUBEB_ERROR) { |
474 | state = CUBEB_STATE_ERROR; | | 475 | state = CUBEB_STATE_ERROR; |
475 | break; | | 476 | break; |
476 | } | | 477 | } |
477 | if (s->play_fd != -1) { | | 478 | if (s->play_fd != -1) { |
478 | pthread_mutex_lock(&s->mutex); | | 479 | pthread_mutex_lock(&s->mutex); |
479 | sun_linear_set_vol(s->play_buf, s->p_info.play.channels, to_write, s->volume); | | 480 | sun_linear_set_vol(s->play_buf, s->p_info.play.channels, to_write, s->volume); |
480 | pthread_mutex_unlock(&s->mutex); | | 481 | pthread_mutex_unlock(&s->mutex); |
481 | } | | 482 | } |
482 | } | | 483 | } |
483 | if (to_write < SUN_BUFFER_FRAMES) { | | 484 | if (to_write < SUN_BUFFER_FRAMES) { |
484 | drain = 1; | | 485 | drain = 1; |
485 | } | | 486 | } |
486 | to_write = s->play_fd != -1 ? to_write : 0; | | 487 | to_write = s->play_fd != -1 ? to_write : 0; |
487 | to_read = s->record_fd != -1 ? SUN_BUFFER_FRAMES : 0; | | 488 | to_read = s->record_fd != -1 ? SUN_BUFFER_FRAMES : 0; |
488 | write_ofs = 0; | | 489 | write_ofs = 0; |
489 | read_ofs = 0; | | 490 | read_ofs = 0; |
490 | while (to_write > 0 || to_read > 0) { | | 491 | while (to_write > 0 || to_read > 0) { |
491 | size_t bytes; | | 492 | size_t bytes; |
492 | ssize_t n, frames; | | 493 | ssize_t n, frames; |
493 | | | 494 | |
494 | if (to_write > 0) { | | 495 | if (to_write > 0) { |
495 | bytes = FRAMES_TO_BYTES(to_write, s->p_info.play.channels); | | 496 | bytes = FRAMES_TO_BYTES(to_write, s->p_info.play.channels); |
496 | if ((n = write(s->play_fd, s->play_buf + write_ofs, bytes)) < 0) { | | 497 | if ((n = write(s->play_fd, s->play_buf + write_ofs, bytes)) < 0) { |
497 | state = CUBEB_STATE_ERROR; | | 498 | state = CUBEB_STATE_ERROR; |
498 | break; | | 499 | break; |
499 | } | | 500 | } |
500 | frames = BYTES_TO_FRAMES(n, s->p_info.play.channels); | | 501 | frames = BYTES_TO_FRAMES(n, s->p_info.play.channels); |
501 | pthread_mutex_lock(&s->mutex); | | 502 | pthread_mutex_lock(&s->mutex); |
502 | s->frames_written += frames; | | 503 | s->frames_written += frames; |
503 | pthread_mutex_unlock(&s->mutex); | | 504 | pthread_mutex_unlock(&s->mutex); |
504 | to_write -= frames; | | 505 | to_write -= frames; |
505 | write_ofs += frames; | | 506 | write_ofs += frames; |
506 | } | | 507 | } |
507 | if (to_read > 0) { | | 508 | if (to_read > 0) { |
508 | bytes = FRAMES_TO_BYTES(to_read, s->r_info.record.channels); | | 509 | bytes = FRAMES_TO_BYTES(to_read, s->r_info.record.channels); |
509 | if ((n = read(s->record_fd, s->record_buf + read_ofs, bytes)) < 0) { | | 510 | if ((n = read(s->record_fd, s->record_buf + read_ofs, bytes)) < 0) { |
510 | state = CUBEB_STATE_ERROR; | | 511 | state = CUBEB_STATE_ERROR; |
511 | break; | | 512 | break; |
512 | } | | 513 | } |
513 | frames = BYTES_TO_FRAMES(n, s->r_info.record.channels); | | 514 | frames = BYTES_TO_FRAMES(n, s->r_info.record.channels); |
514 | to_read -= frames; | | 515 | to_read -= frames; |
515 | read_ofs += frames; | | 516 | read_ofs += frames; |
516 | } | | 517 | } |
517 | } | | 518 | } |
518 | if (drain && state != CUBEB_STATE_ERROR) { | | 519 | if (drain && state != CUBEB_STATE_ERROR) { |
519 | state = CUBEB_STATE_DRAINED; | | 520 | state = CUBEB_STATE_DRAINED; |
520 | break; | | 521 | break; |
521 | } | | 522 | } |
522 | } | | 523 | } |
523 | s->state_cb(s, s->user_ptr, state); | | 524 | s->state_cb(s, s->user_ptr, state); |
524 | return NULL; | | 525 | return NULL; |
525 | } | | 526 | } |
526 | | | 527 | |
527 | static int | | 528 | static int |
528 | sun_stream_init(cubeb * context, | | 529 | sun_stream_init(cubeb * context, |
529 | cubeb_stream ** stream, | | 530 | cubeb_stream ** stream, |
530 | char const * stream_name, | | 531 | char const * stream_name, |
531 | cubeb_devid input_device, | | 532 | cubeb_devid input_device, |
532 | cubeb_stream_params * input_stream_params, | | 533 | cubeb_stream_params * input_stream_params, |
533 | cubeb_devid output_device, | | 534 | cubeb_devid output_device, |
534 | cubeb_stream_params * output_stream_params, | | 535 | cubeb_stream_params * output_stream_params, |
535 | unsigned latency_frames, | | 536 | unsigned latency_frames, |
536 | cubeb_data_callback data_callback, | | 537 | cubeb_data_callback data_callback, |
537 | cubeb_state_callback state_callback, | | 538 | cubeb_state_callback state_callback, |
538 | void * user_ptr) | | 539 | void * user_ptr) |
539 | { | | 540 | { |
540 | int ret = CUBEB_OK; | | 541 | int ret = CUBEB_OK; |
541 | cubeb_stream *s = NULL; | | 542 | cubeb_stream *s = NULL; |
542 | | | 543 | |
543 | (void)stream_name; | | 544 | (void)stream_name; |
544 | (void)latency_frames; | | 545 | (void)latency_frames; |
545 | if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { | | 546 | if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) { |
546 | ret = CUBEB_ERROR; | | 547 | ret = CUBEB_ERROR; |
547 | goto error; | | 548 | goto error; |
548 | } | | 549 | } |
549 | s->record_fd = -1; | | 550 | s->record_fd = -1; |
550 | s->play_fd = -1; | | 551 | s->play_fd = -1; |
551 | if (input_device != 0) { | | 552 | if (input_device != 0) { |
552 | snprintf(s->input_name, sizeof(s->input_name), | | 553 | snprintf(s->input_name, sizeof(s->input_name), |
553 | "/dev/audio%zu", (uintptr_t)input_device - 1); | | 554 | "/dev/audio%zu", (uintptr_t)input_device - 1); |
554 | } else { | | 555 | } else { |
555 | snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE); | | 556 | snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE); |
556 | } | | 557 | } |
557 | if (output_device != 0) { | | 558 | if (output_device != 0) { |
558 | snprintf(s->output_name, sizeof(s->output_name), | | 559 | snprintf(s->output_name, sizeof(s->output_name), |
559 | "/dev/audio%zu", (uintptr_t)output_device - 1); | | 560 | "/dev/audio%zu", (uintptr_t)output_device - 1); |
560 | } else { | | 561 | } else { |
561 | snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE); | | 562 | snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE); |
562 | } | | 563 | } |
563 | if (input_stream_params != NULL) { | | 564 | if (input_stream_params != NULL) { |
564 | #ifndef CUBEB_OLD_API | | 565 | #ifndef CUBEB_OLD_API |
565 | if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { | | 566 | if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { |
566 | LOG("Loopback not supported"); | | 567 | LOG("Loopback not supported"); |
567 | ret = CUBEB_ERROR_NOT_SUPPORTED; | | 568 | ret = CUBEB_ERROR_NOT_SUPPORTED; |
568 | goto error; | | 569 | goto error; |
569 | } | | 570 | } |
570 | #endif | | 571 | #endif |
571 | if (s->record_fd == -1) { | | 572 | if (s->record_fd == -1) { |
572 | if ((s->record_fd = open(s->input_name, O_RDONLY)) == -1) { | | 573 | if ((s->record_fd = open(s->input_name, O_RDONLY)) == -1) { |
573 | LOG("Audio device cannot be opened as read-only"); | | 574 | LOG("Audio device cannot be opened as read-only"); |
574 | ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; | | 575 | ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; |
575 | goto error; | | 576 | goto error; |
576 | } | | 577 | } |
577 | } | | 578 | } |
578 | AUDIO_INITINFO(&s->r_info); | | 579 | AUDIO_INITINFO(&s->r_info); |
579 | #ifdef AUMODE_RECORD | | 580 | #ifdef AUMODE_RECORD |
580 | s->r_info.mode = AUMODE_RECORD; | | 581 | s->r_info.mode = AUMODE_RECORD; |
581 | #endif | | 582 | #endif |
582 | if ((ret = sun_copy_params(s->record_fd, s, input_stream_params, | | 583 | if ((ret = sun_copy_params(s->record_fd, s, input_stream_params, |
583 | &s->r_info, &s->r_info.record)) != CUBEB_OK) { | | 584 | &s->r_info, &s->r_info.record)) != CUBEB_OK) { |
584 | LOG("Setting record params failed"); | | 585 | LOG("Setting record params failed"); |
585 | goto error; | | 586 | goto error; |
586 | } | | 587 | } |
587 | } | | 588 | } |
588 | if (output_stream_params != NULL) { | | 589 | if (output_stream_params != NULL) { |
589 | #ifndef CUBEB_OLD_API | | 590 | #ifndef CUBEB_OLD_API |
590 | if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { | | 591 | if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { |
591 | LOG("Loopback not supported"); | | 592 | LOG("Loopback not supported"); |
592 | ret = CUBEB_ERROR_NOT_SUPPORTED; | | 593 | ret = CUBEB_ERROR_NOT_SUPPORTED; |
593 | goto error; | | 594 | goto error; |
594 | } | | 595 | } |
595 | #endif | | 596 | #endif |
596 | if (s->play_fd == -1) { | | 597 | if (s->play_fd == -1) { |
597 | if ((s->play_fd = open(s->output_name, O_WRONLY)) == -1) { | | 598 | if ((s->play_fd = open(s->output_name, O_WRONLY)) == -1) { |
598 | LOG("Audio device cannot be opened as write-only"); | | 599 | LOG("Audio device cannot be opened as write-only"); |
599 | ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; | | 600 | ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; |
600 | goto error; | | 601 | goto error; |
601 | } | | 602 | } |
602 | } | | 603 | } |
603 | AUDIO_INITINFO(&s->p_info); | | 604 | AUDIO_INITINFO(&s->p_info); |
604 | #ifdef AUMODE_PLAY | | 605 | #ifdef AUMODE_PLAY |
605 | s->p_info.mode = AUMODE_PLAY; | | 606 | s->p_info.mode = AUMODE_PLAY; |
606 | #endif | | 607 | #endif |
607 | if ((ret = sun_copy_params(s->play_fd, s, output_stream_params, | | 608 | if ((ret = sun_copy_params(s->play_fd, s, output_stream_params, |
608 | &s->p_info, &s->p_info.play)) != CUBEB_OK) { | | 609 | &s->p_info, &s->p_info.play)) != CUBEB_OK) { |
609 | LOG("Setting play params failed"); | | 610 | LOG("Setting play params failed"); |
610 | goto error; | | 611 | goto error; |
611 | } | | 612 | } |
612 | } | | 613 | } |
613 | s->context = context; | | 614 | s->context = context; |
614 | s->volume = 1.0; | | 615 | s->volume = 1.0; |
615 | s->state_cb = state_callback; | | 616 | s->state_cb = state_callback; |
616 | s->data_cb = data_callback; | | 617 | s->data_cb = data_callback; |
617 | s->user_ptr = user_ptr; | | 618 | s->user_ptr = user_ptr; |
618 | if (pthread_mutex_init(&s->mutex, NULL) != 0) { | | 619 | if (pthread_mutex_init(&s->mutex, NULL) != 0) { |
619 | LOG("Failed to create mutex"); | | 620 | LOG("Failed to create mutex"); |
620 | goto error; | | 621 | goto error; |
621 | } | | 622 | } |
622 | if (s->play_fd != -1 && (s->play_buf = calloc(SUN_BUFFER_FRAMES, | | 623 | if (s->play_fd != -1 && (s->play_buf = calloc(SUN_BUFFER_FRAMES, |
623 | s->p_info.play.channels * sizeof(int16_t))) == NULL) { | | 624 | s->p_info.play.channels * sizeof(int16_t))) == NULL) { |
624 | ret = CUBEB_ERROR; | | 625 | ret = CUBEB_ERROR; |
625 | goto error; | | 626 | goto error; |
626 | } | | 627 | } |
627 | if (s->record_fd != -1 && (s->record_buf = calloc(SUN_BUFFER_FRAMES, | | 628 | if (s->record_fd != -1 && (s->record_buf = calloc(SUN_BUFFER_FRAMES, |
628 | s->r_info.record.channels * sizeof(int16_t))) == NULL) { | | 629 | s->r_info.record.channels * sizeof(int16_t))) == NULL) { |
629 | ret = CUBEB_ERROR; | | 630 | ret = CUBEB_ERROR; |
630 | goto error; | | 631 | goto error; |
631 | } | | 632 | } |
632 | if (s->floating) { | | 633 | if (s->floating) { |
633 | if (s->play_fd != -1 && (s->f_play_buf = calloc(SUN_BUFFER_FRAMES, | | 634 | if (s->play_fd != -1 && (s->f_play_buf = calloc(SUN_BUFFER_FRAMES, |
634 | s->p_info.play.channels * sizeof(float))) == NULL) { | | 635 | s->p_info.play.channels * sizeof(float))) == NULL) { |
635 | ret = CUBEB_ERROR; | | 636 | ret = CUBEB_ERROR; |
636 | goto error; | | 637 | goto error; |
637 | } | | 638 | } |
638 | if (s->record_fd != -1 && (s->f_record_buf = calloc(SUN_BUFFER_FRAMES, | | 639 | if (s->record_fd != -1 && (s->f_record_buf = calloc(SUN_BUFFER_FRAMES, |
639 | s->r_info.record.channels * sizeof(float))) == NULL) { | | 640 | s->r_info.record.channels * sizeof(float))) == NULL) { |
640 | ret = CUBEB_ERROR; | | 641 | ret = CUBEB_ERROR; |
641 | goto error; | | 642 | goto error; |
642 | } | | 643 | } |
643 | } | | 644 | } |
644 | *stream = s; | | 645 | *stream = s; |
645 | return CUBEB_OK; | | 646 | return CUBEB_OK; |
646 | error: | | 647 | error: |
647 | if (s != NULL) { | | 648 | if (s != NULL) { |
648 | sun_stream_destroy(s); | | 649 | sun_stream_destroy(s); |
649 | } | | 650 | } |
650 | return ret; | | 651 | return ret; |
651 | } | | 652 | } |
652 | | | 653 | |
653 | static int | | 654 | static int |
654 | sun_stream_start(cubeb_stream * s) | | 655 | sun_stream_start(cubeb_stream * s) |
655 | { | | 656 | { |
656 | s->running = 1; | | 657 | s->running = 1; |
657 | if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) { | | 658 | if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) { |
658 | LOG("Couldn't create thread"); | | 659 | LOG("Couldn't create thread"); |
659 | return CUBEB_ERROR; | | 660 | return CUBEB_ERROR; |
660 | } | | 661 | } |
661 | return CUBEB_OK; | | 662 | return CUBEB_OK; |
662 | } | | 663 | } |
663 | | | 664 | |
664 | static int | | 665 | static int |
665 | sun_stream_get_position(cubeb_stream * s, uint64_t * position) | | 666 | sun_stream_get_position(cubeb_stream * s, uint64_t * position) |
666 | { | | 667 | { |
| | | 668 | #ifdef AUDIO_GETOOFFS |
| | | 669 | struct audio_offset offset; |
| | | 670 | |
| | | 671 | if (ioctl(s->play_fd, AUDIO_GETOOFFS, &offset) == -1) { |
| | | 672 | return CUBEB_ERROR; |
| | | 673 | } |
| | | 674 | s->blocks_written += offset.deltablks; |
| | | 675 | *position = BYTES_TO_FRAMES(s->blocks_written * s->p_info.blocksize, |
| | | 676 | s->p_info.play.channels); |
| | | 677 | return CUBEB_OK; |
| | | 678 | #else |
667 | pthread_mutex_lock(&s->mutex); | | 679 | pthread_mutex_lock(&s->mutex); |
668 | *position = s->frames_written; | | 680 | *position = s->frames_written; |
669 | pthread_mutex_unlock(&s->mutex); | | 681 | pthread_mutex_unlock(&s->mutex); |
670 | return CUBEB_OK; | | 682 | return CUBEB_OK; |
| | | 683 | #endif |
671 | } | | 684 | } |
672 | | | 685 | |
673 | static int | | 686 | static int |
674 | sun_stream_get_latency(cubeb_stream * stream, uint32_t * latency) | | 687 | sun_stream_get_latency(cubeb_stream * stream, uint32_t * latency) |
675 | { | | 688 | { |
676 | #ifdef AUDIO_GETBUFINFO | | 689 | #ifdef AUDIO_GETBUFINFO |
677 | struct audio_info info; | | 690 | struct audio_info info; |
678 | | | 691 | |
679 | if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) { | | 692 | if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) { |
680 | return CUBEB_ERROR; | | 693 | return CUBEB_ERROR; |
681 | } | | 694 | } |
682 | | | 695 | |
683 | *latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize, | | 696 | *latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize, |
684 | info.play.channels); | | 697 | info.play.channels); |
685 | return CUBEB_OK; | | 698 | return CUBEB_OK; |
686 | #else | | 699 | #else |
687 | cubeb_stream_params params; | | 700 | cubeb_stream_params params; |
688 | | | 701 | |
689 | params.rate = stream->p_info.play.sample_rate; | | 702 | params.rate = stream->p_info.play.sample_rate; |
690 | | | 703 | |
691 | return sun_get_min_latency(NULL, params, latency); | | 704 | return sun_get_min_latency(NULL, params, latency); |
692 | #endif | | 705 | #endif |
693 | } | | 706 | } |
694 | | | 707 | |
695 | static int | | 708 | static int |
696 | sun_stream_set_volume(cubeb_stream * stream, float volume) | | 709 | sun_stream_set_volume(cubeb_stream * stream, float volume) |
697 | { | | 710 | { |
698 | pthread_mutex_lock(&stream->mutex); | | 711 | pthread_mutex_lock(&stream->mutex); |
699 | stream->volume = volume; | | 712 | stream->volume = volume; |
700 | pthread_mutex_unlock(&stream->mutex); | | 713 | pthread_mutex_unlock(&stream->mutex); |
701 | return CUBEB_OK; | | 714 | return CUBEB_OK; |
702 | } | | 715 | } |
703 | | | 716 | |
704 | static int | | 717 | static int |
705 | sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device) | | 718 | sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device) |
706 | { | | 719 | { |
707 | *device = calloc(1, sizeof(cubeb_device)); | | 720 | *device = calloc(1, sizeof(cubeb_device)); |
708 | if (*device == NULL) { | | 721 | if (*device == NULL) { |
709 | return CUBEB_ERROR; | | 722 | return CUBEB_ERROR; |
710 | } | | 723 | } |
711 | (*device)->input_name = stream->record_fd != -1 ? | | 724 | (*device)->input_name = stream->record_fd != -1 ? |
712 | strdup(stream->input_name) : NULL; | | 725 | strdup(stream->input_name) : NULL; |
713 | (*device)->output_name = stream->play_fd != -1 ? | | 726 | (*device)->output_name = stream->play_fd != -1 ? |
714 | strdup(stream->output_name) : NULL; | | 727 | strdup(stream->output_name) : NULL; |
715 | return CUBEB_OK; | | 728 | return CUBEB_OK; |
716 | } | | 729 | } |
717 | | | 730 | |
718 | static int | | 731 | static int |
719 | sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) | | 732 | sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) |
720 | { | | 733 | { |
721 | (void)stream; | | 734 | (void)stream; |
722 | free(device->input_name); | | 735 | free(device->input_name); |
723 | free(device->output_name); | | 736 | free(device->output_name); |
724 | free(device); | | 737 | free(device); |
725 | return CUBEB_OK; | | 738 | return CUBEB_OK; |
726 | } | | 739 | } |
727 | | | 740 | |
728 | static struct cubeb_ops const sun_ops = { | | 741 | static struct cubeb_ops const sun_ops = { |
729 | .init = sun_init, | | 742 | .init = sun_init, |
730 | .get_backend_id = sun_get_backend_id, | | 743 | .get_backend_id = sun_get_backend_id, |
731 | .get_max_channel_count = sun_get_max_channel_count, | | 744 | .get_max_channel_count = sun_get_max_channel_count, |
732 | .get_min_latency = sun_get_min_latency, | | 745 | .get_min_latency = sun_get_min_latency, |
733 | .get_preferred_sample_rate = sun_get_preferred_sample_rate, | | 746 | .get_preferred_sample_rate = sun_get_preferred_sample_rate, |
734 | #ifndef CUBEB_OLD_API | | 747 | #ifndef CUBEB_OLD_API |
735 | .enumerate_devices = sun_enumerate_devices, | | 748 | .enumerate_devices = sun_enumerate_devices, |
736 | .device_collection_destroy = sun_device_collection_destroy, | | 749 | .device_collection_destroy = sun_device_collection_destroy, |
737 | #endif | | 750 | #endif |
738 | .destroy = sun_destroy, | | 751 | .destroy = sun_destroy, |
739 | .stream_init = sun_stream_init, | | 752 | .stream_init = sun_stream_init, |
740 | .stream_destroy = sun_stream_destroy, | | 753 | .stream_destroy = sun_stream_destroy, |
741 | .stream_start = sun_stream_start, | | 754 | .stream_start = sun_stream_start, |
742 | .stream_stop = sun_stream_stop, | | 755 | .stream_stop = sun_stream_stop, |
743 | #ifndef CUBEB_OLD_API | | 756 | #ifndef CUBEB_OLD_API |
744 | .stream_reset_default_device = NULL, | | 757 | .stream_reset_default_device = NULL, |
745 | #endif | | 758 | #endif |
746 | .stream_get_position = sun_stream_get_position, | | 759 | .stream_get_position = sun_stream_get_position, |
747 | .stream_get_latency = sun_stream_get_latency, | | 760 | .stream_get_latency = sun_stream_get_latency, |
748 | .stream_set_volume = sun_stream_set_volume, | | 761 | .stream_set_volume = sun_stream_set_volume, |
749 | .stream_set_panning = NULL, | | 762 | .stream_set_panning = NULL, |
750 | .stream_get_current_device = sun_get_current_device, | | 763 | .stream_get_current_device = sun_get_current_device, |
751 | .stream_device_destroy = sun_stream_device_destroy, | | 764 | .stream_device_destroy = sun_stream_device_destroy, |
752 | .stream_register_device_changed_callback = NULL, | | 765 | .stream_register_device_changed_callback = NULL, |
753 | .register_device_collection_changed = NULL | | 766 | .register_device_collection_changed = NULL |
754 | }; | | 767 | }; |