e99d42b1 |
1 | /* |
2 | * This file is part of DisOrder |
3 | * Copyright (C) 2007 Richard Kettlewell |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, but |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
18 | * USA |
19 | */ |
20 | /** @file server/speaker-oss.c |
21 | * @brief Support for @ref BACKEND_OSS */ |
22 | |
23 | #include <config.h> |
24 | |
25 | #if HAVE_SYS_SOUNDCARD_H |
26 | |
27 | #include "types.h" |
28 | |
29 | #include <unistd.h> |
30 | #include <poll.h> |
31 | #include <string.h> |
32 | #include <fcntl.h> |
33 | #include <unistd.h> |
34 | #include <errno.h> |
35 | #include <sys/ioctl.h> |
36 | #include <sys/soundcard.h> |
37 | |
38 | #include "configuration.h" |
39 | #include "syscalls.h" |
40 | #include "log.h" |
41 | #include "speaker-protocol.h" |
42 | #include "speaker.h" |
43 | |
44 | /** @brief Current device */ |
45 | static int ossfd = -1; |
46 | |
47 | /** @brief OSS backend initialization */ |
48 | static void oss_init(void) { |
49 | info("selected OSS backend"); |
50 | } |
51 | |
52 | /** @brief OSS deactivation */ |
53 | static void oss_deactivate(void) { |
54 | if(ossfd != -1) { |
55 | xclose(ossfd); |
56 | ossfd = -1; |
57 | device_state = device_closed; |
58 | D(("released audio device")); |
59 | } |
60 | } |
61 | |
62 | /** @brief OSS backend activation */ |
63 | static void oss_activate(void) { |
64 | int stereo, format, rate; |
65 | const char *device; |
66 | |
67 | if(ossfd == -1) { |
68 | /* Try to pick a device */ |
69 | if(!strcmp(config->device, "default")) { |
70 | if(access("/dev/dsp", W_OK) == 0) |
71 | device = "/dev/dsp"; |
72 | else if(access("/dev/audio", W_OK) == 0) |
73 | device = "/dev/audio"; |
74 | } else |
75 | device = config->device; /* just believe the user */ |
76 | /* Open the device */ |
77 | if((ossfd = open(device, O_WRONLY, 0)) < 0) { |
78 | error(errno, "error opening %s", device); |
79 | goto failed; |
80 | } |
81 | /* Set the audio format */ |
82 | stereo = (config->sample_format.channels == 2); |
83 | if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) { |
84 | error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo); |
85 | goto failed; |
86 | } |
87 | if(config->sample_format.bits == 8) |
88 | format = AFMT_U8; |
89 | else if(config->sample_format.bits == 16) |
90 | format = (config->sample_format.endian == ENDIAN_LITTLE |
91 | ? AFMT_S16_LE : AFMT_S16_BE); |
92 | else { |
93 | error(0, "unsupported sample_format for oss backend"); |
94 | goto failed; |
95 | } |
96 | if(ioctl(ossfd, SNDCTL_DSP_SETFMT, &format) < 0) { |
97 | error(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format); |
98 | goto failed; |
99 | } |
100 | rate = config->sample_format.rate; |
101 | if(ioctl(ossfd, SNDCTL_DSP_SPEED, &rate) < 0) { |
102 | error(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate); |
103 | goto failed; |
104 | } |
105 | if((unsigned)rate != config->sample_format.rate) |
106 | error(0, "asked for %luHz, got %dHz", |
107 | (unsigned long)config->sample_format.rate, rate); |
108 | nonblock(ossfd); |
109 | device_state = device_open; |
110 | } |
111 | return; |
112 | failed: |
113 | device_state = device_error; |
114 | if(ossfd >= 0) |
115 | xclose(ossfd); |
116 | } |
117 | |
118 | /** @brief Play via OSS */ |
119 | static size_t oss_play(size_t frames) { |
120 | const size_t bytes_to_play = frames * bpf; |
121 | ssize_t bytes_written; |
122 | |
123 | bytes_written = write(ossfd, playing->buffer + playing->start, |
124 | bytes_to_play); |
125 | if(bytes_written < 0) |
126 | switch(errno) { |
127 | case EINTR: /* interruped */ |
128 | case EAGAIN: /* overrun */ |
129 | return 0; /* try again later */ |
130 | default: |
131 | fatal(errno, "error writing to audio device"); |
132 | } |
133 | return bytes_written / bpf; |
134 | } |
135 | |
136 | static int oss_slot; |
137 | |
138 | /** @brief Fill in poll fd array for OSS */ |
139 | static void oss_beforepoll(void) { |
140 | oss_slot = addfd(ossfd, POLLOUT|POLLERR); |
141 | } |
142 | |
143 | /** @brief Process poll() results for OSS */ |
144 | static int oss_ready(void) { |
145 | return !!(fds[oss_slot].revents & (POLLOUT|POLLERR)); |
146 | } |
147 | |
148 | const struct speaker_backend oss_backend = { |
149 | BACKEND_OSS, |
150 | 0, |
151 | oss_init, |
152 | oss_activate, |
153 | oss_play, |
154 | oss_deactivate, |
155 | oss_beforepoll, |
156 | oss_ready |
157 | }; |
158 | |
159 | #endif |
160 | |
161 | /* |
162 | Local Variables: |
163 | c-basic-offset:2 |
164 | comment-column:40 |
165 | fill-column:79 |
166 | indent-tabs-mode:nil |
167 | End: |
168 | */ |