1 /*
2 * Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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 Foundation,
16 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include <config.h>
20
21 #include <sys/types.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <sys/ioctl.h>
28 #include <errno.h>
29 #include <pthread.h>
30
31 #ifdef __FreeBSD__
32 #include <sys/soundcard.h>
33 #else
34 #include <linux/soundcard.h>
35 #endif
36
37 #include <event.h>
38 #include <log.h>
39 #include <frame.h>
40 #include <stream.h>
41 #include <inputs.h>
42 #include <conf_parse.h>
43
44 struct oss_input {
45 struct stream *output;
46 char device[256];
47 struct soft_queue *queue;
48 struct audio_ring *ring;
49 int fd;
50 int rate;
51 int channels;
52 pthread_t thread;
53 int running;
54 };
55
capture_loop(void * d)56 static void *capture_loop( void *d )
57 {
58 struct oss_input *conf = (struct oss_input *)d;
59 unsigned char *buf;
60 int len = 0, ret, blocksize;
61
62 blocksize = conf->rate * conf->channels * 2 / 50;
63
64 spook_log( SL_VERBOSE, "input-oss: blocksize is %d", blocksize );
65 buf = malloc( blocksize );
66
67 for(;;)
68 {
69 ret = read( conf->fd, buf + len, blocksize - len );
70 if( ret < 0 )
71 {
72 perror( "read" );
73 break;
74 }
75 len += ret;
76 if( len == blocksize )
77 {
78 audio_ring_input( conf->ring, buf, len );
79 len = 0;
80 }
81 }
82 return NULL;
83 }
84
get_back_frame(struct event_info * ei,void * d)85 static void get_back_frame( struct event_info *ei, void *d )
86 {
87 struct oss_input *conf = (struct oss_input *)d;
88 struct frame *f = (struct frame *)ei->data;
89
90 deliver_frame_to_stream( f, conf->output );
91 }
92
get_framerate(struct stream * s,int * fincr,int * fbase)93 static void get_framerate( struct stream *s, int *fincr, int *fbase )
94 {
95 struct oss_input *conf = (struct oss_input *)s->private;
96
97 *fincr = conf->channels;
98 *fbase = conf->rate * conf->channels;
99 }
100
set_running(struct stream * s,int running)101 static void set_running( struct stream *s, int running )
102 {
103 struct oss_input *conf = (struct oss_input *)s->private;
104
105 conf->running = running;
106 }
107
108 /************************ CONFIGURATION DIRECTIVES ************************/
109
start_block(void)110 static void *start_block(void)
111 {
112 struct oss_input *conf;
113
114 conf = (struct oss_input *)malloc( sizeof( struct oss_input ) );
115 conf->output = NULL;
116 conf->device[0] = 0;
117 conf->rate = 0;
118 conf->channels = 2;
119 conf->running = 0;
120
121 return conf;
122 }
123
end_block(void * d)124 static int end_block( void *d )
125 {
126 struct oss_input *conf = (struct oss_input *)d;
127 int i;
128
129 if( ! conf->output )
130 {
131 spook_log( SL_ERR, "oss: missing output stream name" );
132 return -1;
133 }
134 if( ! conf->device[0] )
135 {
136 spook_log( SL_ERR, "oss: missing DSP device name" );
137 return -1;
138 }
139 if( conf->rate == 0 )
140 {
141 spook_log( SL_ERR, "oss: sample rate not specified" );
142 return -1;
143 }
144 if( ( conf->fd = open( conf->device, O_RDONLY ) ) < 0 )
145 {
146 spook_log( SL_ERR, "oss: unable to open %s: %s", conf->device,
147 strerror( errno ) );
148 return -1;
149 }
150 if( ioctl( conf->fd, SNDCTL_DSP_GETFMTS, &i ) < 0 )
151 {
152 spook_log( SL_ERR,
153 "oss: unable to query available sample formats" );
154 return -1;
155 }
156 if( ! ( i & AFMT_S16_BE ) )
157 {
158 spook_log( SL_ERR,
159 "oss: device does not support any usable sample formats" );
160 return -1;
161 }
162 i = AFMT_S16_BE;
163 if( ioctl( conf->fd, SNDCTL_DSP_SETFMT, &i ) < 0 )
164 {
165 spook_log( SL_ERR, "oss: unable to set sample format" );
166 return -1;
167 }
168 i = conf->rate;
169 if( ioctl( conf->fd, SNDCTL_DSP_SPEED, &i ) < 0 )
170 {
171 spook_log( SL_ERR, "oss: unable to set sample rate" );
172 return -1;
173 }
174 i = 1;
175 if( ioctl( conf->fd, SNDCTL_DSP_STEREO, &i ) < 0 )
176 {
177 spook_log( SL_ERR, "oss: unable to set channel count" );
178 return -1;
179 }
180 /* make sure channel count is right!!! */
181 if( ioctl( conf->fd, SOUND_PCM_READ_CHANNELS, &i ) < 0 )
182 {
183 spook_log( SL_ERR, "oss: unable to set channel count" );
184 return -1;
185 }
186 conf->queue = new_soft_queue( 16 );
187 add_softqueue_event( conf->queue, 0, get_back_frame, conf );
188 /* Set frame length to 4608, which is the size of the blocks that
189 * the MP2 encoder will need. This is just temporary... */
190 conf->ring = new_audio_ring( 2 * conf->channels, conf->rate,
191 4608, conf->queue );
192 pthread_create( &conf->thread, NULL, capture_loop, conf );
193
194 return 0;
195 }
196
set_device(int num_tokens,struct token * tokens,void * d)197 static int set_device( int num_tokens, struct token *tokens, void *d )
198 {
199 struct oss_input *conf = (struct oss_input *)d;
200
201 strcpy( conf->device, tokens[1].v.str );
202 return 0;
203 }
204
set_output(int num_tokens,struct token * tokens,void * d)205 static int set_output( int num_tokens, struct token *tokens, void *d )
206 {
207 struct oss_input *conf = (struct oss_input *)d;
208
209 conf->output = new_stream( tokens[1].v.str, FORMAT_PCM, conf );
210 if( ! conf->output )
211 {
212 spook_log( SL_ERR, "oss: unable to create stream \"%s\"",
213 tokens[1].v.str );
214 return -1;
215 }
216 conf->output->get_framerate = get_framerate;
217 conf->output->set_running = set_running;
218 return 0;
219 }
220
set_samplerate(int num_tokens,struct token * tokens,void * d)221 static int set_samplerate( int num_tokens, struct token *tokens, void *d )
222 {
223 struct oss_input *conf = (struct oss_input *)d;
224
225 if( conf->rate > 0 )
226 {
227 spook_log( SL_ERR, "oss: sample rate has already been set!" );
228 return -1;
229 }
230 conf->rate = tokens[1].v.num;
231
232 return 0;
233 }
234
235 static struct statement config_statements[] = {
236 /* directive name, process function, min args, max args, arg types */
237 { "output", set_output, 1, 1, { TOKEN_STR } },
238 { "device", set_device, 1, 1, { TOKEN_STR } },
239 { "samplerate", set_samplerate, 1, 1, { TOKEN_NUM } },
240
241 /* empty terminator -- do not remove */
242 { NULL, NULL, 0, 0, {} }
243 };
244
oss_init(void)245 void oss_init(void)
246 {
247 register_config_context( "input", "oss", start_block, end_block,
248 config_statements );
249 }
250