Replace manual option handling by use of glib
[lcid-xwax.git] / oss.c
blobf1618e11ef9fa7bca5f718234ed54a0504ec0fd2
1 /*
2 * Copyright (C) 2008 Mark Hills <mark@pogo.org.uk>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License version 2 for more details.
13 * You should have received a copy of the GNU General Public License
14 * version 2 along with this program; if not, write to the Free
15 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16 * MA 02110-1301, USA.
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/ioctl.h>
26 #include <sys/poll.h>
27 #include <sys/soundcard.h>
29 #include "device.h"
30 #include "timecoder.h"
31 #include "player.h"
33 #define DEFAULT_OSS_BUFFERS 8
34 #define DEFAULT_OSS_FRAGMENT 7
36 static guint oss_fragment = DEFAULT_OSS_FRAGMENT;
37 static guint oss_buffers = DEFAULT_OSS_BUFFERS;
39 struct oss_t {
40 int fd;
41 struct pollfd *pe;
45 static int clear(struct device_t *dv)
47 int r;
48 struct oss_t *oss = (struct oss_t*)dv->local;
50 r = close(oss->fd);
51 if(r == -1)
52 perror("close");
54 free(dv->local);
56 return r;
60 /* Push audio into the device's buffer, for playback */
62 static int push(int fd, signed short *pcm, int samples)
64 int r, bytes;
66 bytes = samples * DEVICE_CHANNELS * sizeof(short);
68 r = write(fd, pcm, bytes);
70 if(r == -1) {
71 perror("write");
72 return -1;
75 if(r < bytes)
76 fprintf(stderr, "Device output overrun.\n");
78 return r / DEVICE_CHANNELS / sizeof(short);
82 /* Pull audio from the device, for recording */
84 static int pull(int fd, signed short *pcm, int samples)
86 int r, bytes;
88 bytes = samples * DEVICE_CHANNELS * sizeof(short);
90 r = read(fd, pcm, bytes);
92 if(r == -1) {
93 perror("read");
94 return -1;
97 if(r < bytes)
98 fprintf(stderr, "Device input underrun.\n");
100 return r / DEVICE_CHANNELS / sizeof(short);
104 static int handle(struct device_t *dv)
106 signed short pcm[DEVICE_FRAME * DEVICE_CHANNELS];
107 int samples;
108 struct oss_t *oss = (struct oss_t*)dv->local;
110 /* Check input buffer for recording */
112 if(oss->pe->revents & POLLIN) {
113 samples = pull(oss->fd, pcm, DEVICE_FRAME);
114 if(samples == -1)
115 return -1;
117 if(dv->timecoder)
118 timecoder_submit(dv->timecoder, pcm, samples);
121 /* Check the output buffer for playback */
123 if(oss->pe->revents & POLLOUT) {
125 /* Always push some audio to the soundcard, even if it means
126 * silence. This has shown itself to be much more reliable
127 * than starting and stopping -- which can affect other
128 * devices in the system. */
130 if(dv->player)
131 player_collect(dv->player, pcm, DEVICE_FRAME);
132 else
133 memset(pcm, 0, DEVICE_FRAME * DEVICE_CHANNELS * sizeof(short));
135 samples = push(oss->fd, pcm, DEVICE_FRAME);
137 if(samples == -1)
138 return -1;
141 return 0;
145 int pollfds(struct device_t *dv, struct pollfd *pe, int n)
147 struct oss_t *oss = (struct oss_t*)dv->local;
149 if(n < 1)
150 return -1;
152 pe->fd = oss->fd;
153 pe->events = POLLIN | POLLOUT | POLLHUP;
155 oss->pe = pe;
157 return 1;
161 int oss_init(struct device_t *dv, const char *filename,
162 unsigned short buffers, unsigned short fragment)
164 int p, fd;
165 struct oss_t *oss;
167 fd = open(filename, O_RDWR, 0);
168 if(fd == -1) {
169 perror("open");
170 return -1;
173 /* Ideally would set independent settings for the record and
174 * playback buffers. Recording needs to buffer where necessary, as
175 * lost audio results in zero-crossings which corrupt the
176 * timecode. Playback buffer neews to be short to avoid high
177 * latency */
179 p = (buffers << 16) | fragment;
180 if(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &p) == -1) {
181 perror("SNDCTL_DSP_SETFRAGMENT");
182 goto fail;
185 p = AFMT_S16_LE;
186 if(ioctl(fd, SNDCTL_DSP_SETFMT, &p) == -1) {
187 perror("SNDCTL_DSP_SETFMT");
188 goto fail;
191 p = DEVICE_CHANNELS;
192 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &p) == -1) {
193 perror("SNDCTL_DSP_CHANNELS");
194 goto fail;
197 p = DEVICE_RATE;
198 if(ioctl(fd, SNDCTL_DSP_SPEED, &p) == -1) {
199 perror("SNDCTL_DSP_SPEED");
200 goto fail;
203 if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
204 perror("fcntl");
205 return -1;
208 dv->local = malloc(sizeof(struct oss_t));
209 if(!dv->local) {
210 perror("malloc");
211 goto fail;
214 oss = (struct oss_t*)dv->local;
216 oss->fd = fd;
217 oss->pe = NULL;
219 dv->pollfds = pollfds;
220 dv->handle = handle;
221 dv->start = NULL;
222 dv->stop = NULL;
223 dv->clear = clear;
225 return 0;
227 fail:
228 close(fd);
229 return -1;
232 static gboolean parse_oss_device(const gchar *option_name, const gchar *value, gpointer data, GError **error)
234 struct device_t* device = createDevice(error);
236 if(!device)
237 return FALSE;
239 return connectAudio(device, oss_init(device, value, oss_buffers, oss_fragment));
242 static GOptionEntry ossOptions[] =
244 { "oss_device", 'o', 0, G_OPTION_ARG_CALLBACK, &parse_oss_device, "Build a deck connected to OSS audio device", "/dev/dsp"},
245 // Set number of buffers for subsequent devices
246 { "buffer_count", 'b', 0, G_OPTION_ARG_INT, &oss_buffers, "Number of buffers", NULL},
247 { "buffer_size", 'f', 0, G_OPTION_ARG_INT, &oss_fragment, "Buffer size to request (2^n bytes)", NULL},
248 { NULL }
251 GOptionGroup* get_oss_option_group()
253 GOptionGroup* group = g_option_group_new
255 "oss",
256 "OSS device options",
257 "Show OSS help options",
258 NULL,
259 NULL
262 if(ossOptions[1].arg_description == NULL)
263 ossOptions[1].arg_description = g_strdup_printf("%u", DEFAULT_OSS_BUFFERS);
265 if(ossOptions[2].arg_description == NULL)
266 ossOptions[2].arg_description = g_strdup_printf("%u", DEFAULT_OSS_FRAGMENT);
268 g_option_group_add_entries(group, ossOptions);
269 return group;