Rename get_bits1() to get_bit().
[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
15 #include "para.h"
16 #include "fd.h"
17 #include "string.h"
18 #include "list.h"
19 #include "sched.h"
20 #include "ggo.h"
21 #include "write.h"
22 #include "oss_write.cmdline.h"
23 #include "error.h"
24
25 /** Always use 16 bit little endian. */
26 #define FORMAT AFMT_S16_LE
27
28 /** Data specific to the oss writer. */
29 struct private_oss_write_data {
30         /** The file handle of the device. */
31         int fd;
32         /**
33          * The samplerate given by command line option or the decoder
34          * of the writer node group.
35          */
36         int samplerate;
37         /**
38          * The number of channels, given by command line option or the
39          * decoder of the writer node group.
40          */
41         int channels;
42         /** Four bytes for stereo streams, two bytes for mono streams. */
43         int bytes_per_frame;
44 };
45
46 static int oss_pre_select(struct sched *s, struct writer_node *wn)
47 {
48         struct private_oss_write_data *powd = wn->private_data;
49         struct writer_node_group *wng = wn->wng;
50
51         if (*wng->loaded - wn->written < powd->bytes_per_frame)
52                 return 0;
53         para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
54         return 1;
55 }
56
57 static int oss_post_select(struct sched *s, struct writer_node *wn)
58 {
59         int ret;
60         struct private_oss_write_data *powd = wn->private_data;
61         struct writer_node_group *wng = wn->wng;
62         size_t frames, bytes = *wng->loaded - wn->written;
63         char *data = *wng->bufp + wn->written;
64
65         if (*wng->input_error < 0 && bytes < powd->bytes_per_frame) {
66                 wn->written = *wng->loaded;
67                 return *wng->input_error;
68         }
69         frames = bytes / powd->bytes_per_frame;
70         if (!frames) /* less than a single frame available */
71                 goto out;
72         if (!FD_ISSET(powd->fd, &s->wfds))
73                 goto out;
74         ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0);
75         if (ret < 0)
76                 return ret;
77         wn->written += ret;
78 out:
79         return 1;
80 }
81
82 static void oss_close(struct writer_node *wn)
83 {
84         struct private_oss_write_data *powd = wn->private_data;
85
86         close(powd->fd);
87         free(powd);
88 }
89
90 /*
91  * The Open Sound System Programmer's Guide sayeth:
92  *
93  * Set sampling parameters always so that number of channels (mono/stereo) is
94  * set before selecting sampling rate (speed).  Failing to do this will make
95  * your program incompatible with cards such as the SoundBlaster Pro which
96  * supports 44.1 kHz in mono but just 22.05 kHz in stereo. A program which
97  * selects 44.1 kHz speed and then sets the device to stereo mode will
98  * incorrectly believe that the device is still in 44.1 kHz mode when actually
99  * the speed is decreased to 22.05 kHz.
100  */
101 static int oss_open(struct writer_node *wn)
102 {
103         int ret, format = FORMAT, channels, samplerate;
104         struct oss_write_args_info *conf = wn->conf;
105         struct writer_node_group *wng = wn->wng;
106         struct private_oss_write_data *powd;
107
108         PARA_INFO_LOG("opening %s\n", conf->device_arg);
109         ret = para_open(conf->device_arg, O_WRONLY, 0);
110         if (ret < 0)
111                 return ret;
112         powd = para_calloc(sizeof(*powd));
113         wn->private_data = powd;
114         powd->fd = ret;
115         ret = mark_fd_nonblocking(powd->fd);
116         if (ret < 0)
117                 goto err;
118         /* set PCM format */
119         ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
120         if (ret < 0) {
121                 ret = -ERRNO_TO_PARA_ERROR(errno);
122                 goto err;
123         }
124         ret = -E_BAD_SAMPLE_FORMAT;
125         if (format != FORMAT)
126                 goto err;
127         /* set number of channels */
128         if (!conf->channels_given && wng->channels)
129                 channels = *wng->channels;
130         else
131                 channels = conf->channels_arg;
132         ret = -E_BAD_CHANNEL_COUNT;
133         if (channels == 0)
134                 goto err;
135         powd->channels = channels;
136         ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &channels);
137         if (ret < 0) {
138                 ret = -ERRNO_TO_PARA_ERROR(errno);
139                 goto err;
140         }
141         if (powd->channels != channels)
142                 goto err;
143         powd->bytes_per_frame = channels * 2;
144
145         /*
146          * Set sampling rate
147          *
148          * If we request a higher sampling rate than is supported by the
149          * device, the the highest possible speed is automatically used. The
150          * value actually used is returned as the new value of the argument.
151          */
152         if (!conf->samplerate_given && wng->samplerate)
153                 samplerate = *wng->samplerate;
154         else
155                 samplerate = conf->samplerate_arg;
156         powd->samplerate = samplerate;
157         ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &samplerate);
158         if (ret < 0) {
159                 ret = -ERRNO_TO_PARA_ERROR(errno);
160                 goto err;
161         }
162         if (samplerate != powd->samplerate) {
163                 int min = PARA_MIN(samplerate, powd->samplerate),
164                         max = PARA_MAX(samplerate, powd->samplerate);
165                 /*
166                  * Check whether the returned sample rate differs significantly
167                  * from the requested one.
168                  */
169                 ret = -E_BAD_SAMPLERATE;
170                 if (100 * max > 110 * min) /* more than 10% deviation */
171                         goto err;
172                 PARA_NOTICE_LOG("using %dHz rather than %dHz\n", samplerate,
173                         powd->samplerate);
174         }
175
176         return 1;
177 err:
178         close(powd->fd);
179         free(powd);
180         return ret;
181 }
182
183 __malloc static void *oss_parse_config(const char *options)
184 {
185         int ret;
186         struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
187
188         PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t"));
189         ret = oss_cmdline_parser_string(options, conf, "oss_write");
190         if (ret)
191                 goto err_out;
192         return conf;
193 err_out:
194         free(conf);
195         return NULL;
196 }
197
198 /**
199  * The init function of the oss writer.
200  *
201  * \param w Pointer to the writer to initialize.
202  *
203  * \sa struct writer.
204  */
205 void oss_write_init(struct writer *w)
206 {
207         struct oss_write_args_info dummy;
208
209         oss_cmdline_parser_init(&dummy);
210         w->open = oss_open;
211         w->close = oss_close;
212         w->pre_select = oss_pre_select;
213         w->post_select = oss_post_select;
214         w->parse_config = oss_parse_config;
215         w->shutdown = NULL;
216         w->help = (struct ggo_help) {
217                 .short_help = oss_write_args_info_help,
218                 .detailed_help = oss_write_args_info_detailed_help
219         };
220         oss_cmdline_parser_free(&dummy);
221 }