Make writers remove btr node on errors.
[paraslash.git] / oss_write.c
1 /*
2 * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file oss_write.c Paraslash's oss output plugin. */
8
9 #include <regex.h>
10 #include <sys/ioctl.h>
11 #include <fcntl.h>
12 #include <dirent.h>
13 #include <sys/soundcard.h>
14 #include <stdbool.h>
15
16 #include "para.h"
17 #include "fd.h"
18 #include "string.h"
19 #include "list.h"
20 #include "sched.h"
21 #include "ggo.h"
22 #include "buffer_tree.h"
23 #include "write.h"
24 #include "write_common.h"
25 #include "oss_write.cmdline.h"
26 #include "error.h"
27
28 /** Always use 16 bit little endian. */
29 #define FORMAT AFMT_S16_LE
30
31 /** Data specific to the oss writer. */
32 struct private_oss_write_data {
33 /** The file handle of the device. */
34 int fd;
35 /**
36 * The samplerate given by command line option or the decoder
37 * of the writer node group.
38 */
39 int samplerate;
40 /**
41 * The number of channels, given by command line option or the
42 * decoder of the writer node group.
43 */
44 int channels;
45 /** Four bytes for stereo streams, two bytes for mono streams. */
46 int bytes_per_frame;
47 };
48
49 static int oss_pre_select(struct sched *s, struct writer_node *wn)
50 {
51 struct private_oss_write_data *powd = wn->private_data;
52 struct writer_node_group *wng = wn->wng;
53
54 if (*wng->loaded - wn->written < powd->bytes_per_frame)
55 return 0;
56 para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
57 return 1;
58 }
59
60 static void oss_pre_select_btr(struct sched *s, struct task *t)
61 {
62 struct writer_node *wn = container_of(t, struct writer_node, task);
63 struct private_oss_write_data *powd = wn->private_data;
64 int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
65
66 t->error = 0;
67 if (ret < 0)
68 goto min_delay;
69 if (ret == 0)
70 return;
71 if (!powd)
72 goto min_delay;
73 para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
74 return;
75 min_delay:
76 s->timeout.tv_sec = 0;
77 s->timeout.tv_usec = 1;
78 }
79
80 static int oss_post_select(struct sched *s, struct writer_node *wn)
81 {
82 int ret;
83 struct private_oss_write_data *powd = wn->private_data;
84 struct writer_node_group *wng = wn->wng;
85 size_t frames, bytes = *wng->loaded - wn->written;
86 char *data = *wng->bufp + wn->written;
87
88 if (*wng->input_error < 0 && bytes < powd->bytes_per_frame) {
89 wn->written = *wng->loaded;
90 return *wng->input_error;
91 }
92 frames = bytes / powd->bytes_per_frame;
93 if (!frames) /* less than a single frame available */
94 goto out;
95 if (!FD_ISSET(powd->fd, &s->wfds))
96 goto out;
97 ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0);
98 if (ret < 0)
99 return ret;
100 wn->written += ret;
101 out:
102 return 1;
103 }
104
105 static void oss_close(struct writer_node *wn)
106 {
107 struct private_oss_write_data *powd = wn->private_data;
108
109 close(powd->fd);
110 oss_cmdline_parser_free(wn->conf);
111 free(powd);
112 }
113
114 /*
115 * The Open Sound System Programmer's Guide sayeth:
116 *
117 * Set sampling parameters always so that number of channels (mono/stereo) is
118 * set before selecting sampling rate (speed). Failing to do this will make
119 * your program incompatible with cards such as the SoundBlaster Pro which
120 * supports 44.1 kHz in mono but just 22.05 kHz in stereo. A program which
121 * selects 44.1 kHz speed and then sets the device to stereo mode will
122 * incorrectly believe that the device is still in 44.1 kHz mode when actually
123 * the speed is decreased to 22.05 kHz.
124 */
125 static int oss_init(struct writer_node *wn, unsigned samplerate, unsigned channels)
126 {
127 int ret, format = FORMAT, ch, rate;
128 struct oss_write_args_info *conf = wn->conf;
129 struct private_oss_write_data *powd = wn->private_data;
130
131 PARA_INFO_LOG("opening %s\n", conf->device_arg);
132 ret = para_open(conf->device_arg, O_WRONLY, 0);
133 if (ret < 0)
134 return ret;
135 powd->fd = ret;
136 ret = mark_fd_nonblocking(powd->fd);
137 if (ret < 0)
138 goto err;
139 /* set PCM format */
140 ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
141 if (ret < 0) {
142 ret = -ERRNO_TO_PARA_ERROR(errno);
143 goto err;
144 }
145 ret = -E_BAD_SAMPLE_FORMAT;
146 if (format != FORMAT)
147 goto err;
148 /* set number of channels */
149 if (!conf->channels_given && channels != 0)
150 ch = channels;
151 else
152 ch = conf->channels_arg;
153 ret = -E_BAD_CHANNEL_COUNT;
154 if (ch == 0)
155 goto err;
156 powd->channels = ch;
157 ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &ch);
158 if (ret < 0) {
159 ret = -ERRNO_TO_PARA_ERROR(errno);
160 goto err;
161 }
162 if (powd->channels != ch)
163 goto err;
164 powd->bytes_per_frame = ch * 2;
165
166 /*
167 * Set sampling rate
168 *
169 * If we request a higher sampling rate than is supported by the
170 * device, the the highest possible speed is automatically used. The
171 * value actually used is returned as the new value of the argument.
172 */
173 if (!conf->samplerate_given && samplerate != 0)
174 rate = samplerate;
175 else
176 rate = conf->samplerate_arg;
177 powd->samplerate = rate;
178 ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &rate);
179 if (ret < 0) {
180 ret = -ERRNO_TO_PARA_ERROR(errno);
181 goto err;
182 }
183 if (rate != powd->samplerate) {
184 int min = PARA_MIN(rate, powd->samplerate),
185 max = PARA_MAX(rate, powd->samplerate);
186 /*
187 * Check whether the returned sample rate differs significantly
188 * from the requested one.
189 */
190 ret = -E_BAD_SAMPLERATE;
191 if (100 * max > 110 * min) /* more than 10% deviation */
192 goto err;
193 PARA_NOTICE_LOG("using %dHz rather than %dHz\n", rate,
194 powd->samplerate);
195 }
196 wn->min_iqs = powd->bytes_per_frame;
197 return 1;
198 err:
199 close(powd->fd);
200 free(powd);
201 return ret;
202 }
203
204 static void oss_post_select_btr(__a_unused struct sched *s,
205 struct task *t)
206 {
207 struct writer_node *wn = container_of(t, struct writer_node, task);
208 struct oss_write_args_info *conf = wn->conf;
209 struct private_oss_write_data *powd = wn->private_data;
210 struct btr_node *btrn = wn->btrn;
211 size_t frames, bytes;
212 int ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
213 char *data;
214
215 if (ret < 0)
216 goto out;
217 if (ret == 0)
218 return;
219 if (powd->fd < 0) {
220 int32_t rate, ch;
221 ret = -1;
222 if (!conf->samplerate_given) /* config option trumps btr_exec */
223 ret = get_btr_samplerate(wn->btrn, &rate);
224 if (ret < 0)
225 rate = conf->samplerate_arg;
226 ret = -1;
227 if (!conf->channels_given)
228 ret = get_btr_channels(wn->btrn, &ch);
229 if (ret < 0)
230 ch = conf->channels_arg;
231 ret = oss_init(wn, rate, ch);
232 if (ret < 0)
233 goto out;
234 return;
235 }
236 bytes = btr_next_buffer(btrn, &data);
237 frames = bytes / powd->bytes_per_frame;
238 if (!frames) { /* eof and less than a single frame available */
239 ret = -E_OSS_EOF;
240 goto out;
241 }
242 ret = 0;
243 if (!FD_ISSET(powd->fd, &s->wfds))
244 goto out;
245 ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0);
246 if (ret < 0)
247 goto out;
248 btr_consume(btrn, ret);
249 ret = 0;
250 out:
251 t->error = ret;
252 if (ret < 0)
253 btr_remove_node(btrn);
254 }
255
256 static int oss_open(struct writer_node *wn)
257 {
258 struct writer_node_group *wng;
259 struct private_oss_write_data *powd;
260
261 powd = para_calloc(sizeof(*powd));
262 wn->private_data = powd;
263 if (!wn->btrn) {
264 wng = wn->wng;
265 return oss_init(wn, wng->samplerate ? *wng->samplerate : 0,
266 wng->channels? *wng->channels : 0);
267 }
268 powd->fd = -1;
269 return 1;
270 }
271
272 __malloc static void *oss_parse_config(const char *options)
273 {
274 int ret;
275 struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
276
277 PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t"));
278 ret = oss_cmdline_parser_string(options, conf, "oss_write");
279 if (ret)
280 goto err_out;
281 return conf;
282 err_out:
283 free(conf);
284 return NULL;
285 }
286
287 static void oss_free_config(void *conf)
288 {
289 oss_cmdline_parser_free(conf);
290 }
291
292 /**
293 * The init function of the oss writer.
294 *
295 * \param w Pointer to the writer to initialize.
296 *
297 * \sa struct writer.
298 */
299 void oss_write_init(struct writer *w)
300 {
301 struct oss_write_args_info dummy;
302
303 oss_cmdline_parser_init(&dummy);
304 w->open = oss_open;
305 w->close = oss_close;
306 w->pre_select = oss_pre_select;
307 w->pre_select_btr = oss_pre_select_btr;
308 w->post_select = oss_post_select;
309 w->post_select_btr = oss_post_select_btr;
310 w->parse_config = oss_parse_config;
311 w->free_config = oss_free_config;
312 w->shutdown = NULL;
313 w->help = (struct ggo_help) {
314 .short_help = oss_write_args_info_help,
315 .detailed_help = oss_write_args_info_detailed_help
316 };
317 oss_cmdline_parser_free(&dummy);
318 }