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