Handle streams separately in tree_add_track()
[cmus.git] / oss.c
blob753c2935e5521d185817ff26e44a047882eaec07
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "op.h"
21 #include "sf.h"
22 #include "xmalloc.h"
24 #if defined(__OpenBSD__)
25 #include <soundcard.h>
26 #else
27 #include <sys/soundcard.h>
28 #endif
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <fcntl.h>
35 static sample_format_t oss_sf;
36 static int oss_fd = -1;
38 /* configuration */
39 static char *oss_dsp_device = NULL;
41 static int oss_close(void);
43 static int oss_reset(void)
45 if (fcntl(oss_fd, SNDCTL_DSP_RESET, 0) == -1) {
46 return -1;
48 return 0;
51 static int oss_set_sf(sample_format_t sf)
53 int tmp, log2_fragment_size, nr_fragments, bytes_per_second;
55 oss_reset();
56 oss_sf = sf;
57 tmp = sf_get_channels(oss_sf) - 1;
58 if (ioctl(oss_fd, SNDCTL_DSP_STEREO, &tmp) == -1)
59 return -1;
61 if (sf_get_bits(oss_sf) == 16) {
62 if (sf_get_signed(oss_sf)) {
63 if (sf_get_bigendian(oss_sf)) {
64 tmp = AFMT_S16_BE;
65 } else {
66 tmp = AFMT_S16_LE;
68 } else {
69 if (sf_get_bigendian(oss_sf)) {
70 tmp = AFMT_U16_BE;
71 } else {
72 tmp = AFMT_U16_LE;
75 } else if (sf_get_bits(oss_sf) == 8) {
76 if (sf_get_signed(oss_sf)) {
77 tmp = AFMT_S8;
78 } else {
79 tmp = AFMT_U8;
81 } else {
82 return -1;
84 if (ioctl(oss_fd, SNDCTL_DSP_SAMPLESIZE, &tmp) == -1)
85 return -1;
87 tmp = sf_get_rate(oss_sf);
88 if (ioctl(oss_fd, SNDCTL_DSP_SPEED, &tmp) == -1)
89 return -1;
91 bytes_per_second = sf_get_second_size(oss_sf);
92 log2_fragment_size = 0;
93 while (1 << log2_fragment_size < bytes_per_second / 25)
94 log2_fragment_size++;
95 log2_fragment_size--;
96 nr_fragments = 32;
98 /* bits 0..15 = size of fragment, 16..31 = log2(number of fragments) */
99 tmp = (nr_fragments << 16) + log2_fragment_size;
100 if (ioctl(oss_fd, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
101 return -1;
102 return 0;
105 static int oss_device_exists(const char *device)
107 struct stat s;
109 if (stat(device, &s))
110 return 0;
111 return 1;
114 static int oss_init(void)
116 const char *new_dsp_dev = "/dev/sound/dsp";
117 const char *dsp_dev = "/dev/dsp";
119 if (oss_dsp_device) {
120 if (oss_device_exists(oss_dsp_device))
121 return 0;
122 free(oss_dsp_device);
123 oss_dsp_device = NULL;
124 return -1;
126 if (oss_device_exists(new_dsp_dev)) {
127 oss_dsp_device = xstrdup(new_dsp_dev);
128 return 0;
130 if (oss_device_exists(dsp_dev)) {
131 oss_dsp_device = xstrdup(dsp_dev);
132 return 0;
134 return -1;
137 static int oss_exit(void)
139 free(oss_dsp_device);
140 oss_dsp_device = NULL;
141 return 0;
144 static int oss_open(sample_format_t sf)
146 oss_fd = open(oss_dsp_device, O_WRONLY);
147 if (oss_fd == -1)
148 return -1;
149 if (oss_set_sf(sf) == -1) {
150 oss_close();
151 return -1;
153 return 0;
156 static int oss_close(void)
158 close(oss_fd);
159 oss_fd = -1;
160 return 0;
163 static int oss_write(const char *buffer, int count)
165 int rc;
167 count /= 4;
168 count *= 4;
169 rc = write(oss_fd, buffer, count);
170 return rc;
173 static int oss_pause(void)
175 if (ioctl(oss_fd, SNDCTL_DSP_POST, NULL) == -1)
176 return -1;
177 return 0;
180 static int oss_unpause(void)
182 return 0;
185 static int oss_buffer_space(void)
187 audio_buf_info info;
188 int space;
190 if (ioctl(oss_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
191 return -1;
192 space = info.bytes;
193 return space;
196 static int op_oss_set_option(int key, const char *val)
198 switch (key) {
199 case 0:
200 free(oss_dsp_device);
201 oss_dsp_device = xstrdup(val);
202 break;
203 default:
204 return -OP_ERROR_NOT_OPTION;
206 return 0;
209 static int op_oss_get_option(int key, char **val)
211 switch (key) {
212 case 0:
213 if (oss_dsp_device)
214 *val = xstrdup(oss_dsp_device);
215 break;
216 default:
217 return -OP_ERROR_NOT_OPTION;
219 return 0;
222 const struct output_plugin_ops op_pcm_ops = {
223 .init = oss_init,
224 .exit = oss_exit,
225 .open = oss_open,
226 .close = oss_close,
227 .write = oss_write,
228 .pause = oss_pause,
229 .unpause = oss_unpause,
230 .buffer_space = oss_buffer_space,
231 .set_option = op_oss_set_option,
232 .get_option = op_oss_get_option
235 const char * const op_pcm_options[] = {
236 "device",
237 NULL
240 const int op_priority = 1;