1 /* OpenCP Module Player
2 * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3 *
4 * Samples device for OSS input
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * revision history: (please note changes here)
21 * -ss040906 Stian Skjelstad <stian@nixia.no>
22 * -first release
23 */
24
25 #include "config.h"
26 #include <errno.h>
27 #include <fcntl.h>
28 #ifdef HAVE_SYS_SOUNDCARD_H
29 #include <sys/soundcard.h>
30 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include "types.h"
39 #include "boot/plinkman.h"
40 #include "boot/psetting.h"
41 #include "dev/devigen.h"
42 #include "dev/imsdev.h"
43 #include "dev/sampler.h"
44 #include "stuff/imsrtns.h"
45 #ifdef SMP_DEBUG
46 #include "stuff/poutput.h"
47 #endif
48
49 #define REVSTEREO 1
50
51 extern struct sounddevice smpOSS;
52 static struct deviceinfo currentcard;
53
54 static int fd_dsp=-1, fd_mixer=-1;
55 static unsigned char *sampbuf;
56 static int buflen, bufpos;
57
58 static int stereo;
59 static int bit16;
60
61 static int igain;
62
getbufpos(void)63 static int getbufpos(void)
64 {
65 while (1)
66 {
67 int target, result;
68 if (bufpos==buflen)
69 bufpos=0;
70 target=buflen-bufpos;
71 result=read(fd_dsp, sampbuf+bufpos, target);
72 if (result>0)
73 bufpos+=result;
74 if (result!=target) /* We don't care if we overrun buffer */
75 break;
76 }
77 return bufpos;
78 }
79
SetOptions(int rate,int opt)80 static void SetOptions(int rate, int opt)
81 {
82 int tmp;
83
84 int newopt;
85
86 int fd;
87
88 if (fd_dsp<0)
89 {
90 if ((fd=open(currentcard.path, O_RDONLY|O_NONBLOCK))<0)
91 {
92 #ifdef OSS_DEBUG
93 fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", currentcard.path, strerror(errno));
94 #endif
95 smpRate=rate; /* damn it... device currently busy */
96 smpOpt=opt;
97 return;
98 }
99 } else
100 fd=fd_dsp;
101
102 if (opt&SMP_16BIT)
103 tmp=16;
104 else
105 tmp=8;
106 if (ioctl(fd, SOUND_PCM_WRITE_BITS, &tmp)<0)
107 {
108 #ifdef OSS_DEBUG
109 perror("devsoss: ioctl(fd_dsp, SOUND_PCM_WRITE_BITS, &tmp)");
110 #endif
111 }
112 if ((bit16=(tmp==16)))
113 newopt = SMP_16BIT | SMP_SIGNEDOUT;
114 else
115 newopt = 0;
116
117 if (opt&SMP_STEREO)
118 tmp=2;
119 else
120 tmp=1;
121 if (ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &tmp)<0)
122 {
123 #ifdef OSS_DEBUG
124 perror("devsoss: ioctl(fd_dsp, SOUND_PCM_WRITE_CHANNELS, tmp)");
125 #endif
126 }
127 if ((stereo=(tmp==2)))
128 newopt |= SMP_STEREO;
129
130 if (ioctl(fd, SOUND_PCM_WRITE_RATE, &rate)<0)
131 {
132 #ifdef OSS_DEBUG
133 perror("devposs: ioctl(fd_dsp, SOUND_PCM_WRITE_RATE, rate)");
134 #endif
135 }
136
137 if (currentcard.opt&REVSTEREO)
138 newopt|=SMP_REVERSESTEREO;
139
140 smpRate=rate;
141 smpOpt=newopt;
142
143 if (fd_dsp<0) /* nasty hack */
144 close(fd);
145 }
146
ossSetSource(int src)147 static void ossSetSource(int src)
148 {
149 int oss_src;
150 if (fd_mixer<0)
151 return;
152 switch (src)
153 {
154 case SMP_MIC:
155 oss_src=1<<SOUND_MIXER_MIC;
156 break;
157 case SMP_LINEIN:
158 oss_src=1<<SOUND_MIXER_LINE;
159 break;
160 default: /* SMP_CD: */
161 oss_src=1<<SOUND_MIXER_CD;
162 }
163 if ((ioctl(fd_mixer, SOUND_MIXER_WRITE_RECSRC, &oss_src)))
164 {
165 #ifdef OSS_DEBUG
166 perror("devsoss: ioctl(fd_mixer, SOUND_MIXER_WRITE_RECSRC, &oss_src)");
167 #endif
168 }
169 }
170
ossSample(unsigned char ** buf,int * len)171 static int ossSample(unsigned char **buf, int *len)
172 {
173 #ifdef OSS_DEBUG
174 int tmp;
175 #endif
176
177 if (*len>65536)
178 *len=65536;
179 if (*len<4096)
180 *len=4096;
181
182 *buf=sampbuf=malloc(*len);
183
184 memsetd(*buf, (smpOpt&SMP_SIGNEDOUT)?0:(smpOpt&SMP_16BIT)?0x80008000:0x80808080, (*len)>>2);
185
186 buflen=*len;
187 bufpos=0;
188 smpGetBufPos=getbufpos;
189
190 if ((fd_dsp=open(currentcard.path, O_RDONLY|O_NONBLOCK))<0)
191 {
192 #ifdef OSS_DEBUG
193 fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", currentcard.path, strerror(errno));
194 sleep(3);
195 #endif
196 return 0;
197 }
198 if (fcntl(fd_dsp, F_SETFD, FD_CLOEXEC)<0)
199 perror("devsoss: fcntl(fd_dsp, F_SETFD, FD_CLOEXEC)");
200 #ifdef OSS_DEBUG
201 #if defined(OSS_GETVERSION)
202 if (ioctl(fd_dsp, OSS_GETVERSION, &tmp)<0)
203 tmp=0;
204 if (tmp<361)
205 tmp= ((tmp&0xf00)<<8) | ((tmp&0xf0)<<4) | (tmp&0xf);
206
207 fprintf(stderr, "devsoss: compiled agains OSS version %d.%d.%d, version %d.%d.%d detected\n", (SOUND_VERSION&0xff0000)>>16, (SOUND_VERSION&0xff00)>>8, SOUND_VERSION&0xff, (tmp&0xff0000)>>16, (tmp&0xff00)>>8, tmp&0xff);
208 #elif defined(SOUND_VERSION)
209 fprintf(stderr, "devsoss: compiled agains OSS version %d.%d.%d\n", (SOUND_VERSION&0xff0000)>>16, (SOUND_VERSION&0xff00)>>8, SOUND_VERSION&0xff);
210 #endif
211 #endif
212
213 #ifdef SMP_DEBUG
214 smpDebug=ossDebug;
215 #endif
216
217 smpSetOptions(smpRate, smpOpt); /* nasty hack */
218
219 if ((fd_mixer>=0)&&igain>=0)
220 {
221 int tmp;
222 if (igain>100)
223 igain=100;
224 tmp=((uint8_t)igain)|(((uint8_t)igain)<<8);
225 if (ioctl(fd_mixer, SOUND_MIXER_WRITE_IGAIN, &tmp))
226 {
227 #ifdef OSS_DEBUG
228 perror("devsoss: ioctl(fd_mixer, SOUND_MIXER_WRITE_IGAIN, &tmp)");
229 #endif
230 }
231 }
232
233 return 1;
234 }
235
ossStop(void)236 static void ossStop(void)
237 {
238 free(sampbuf);
239 #ifdef SMP_DEBUG
240 smpDebug=0;
241 #endif
242 if (fd_dsp)
243 {
244 close(fd_dsp);
245 fd_dsp=-1;
246 }
247 }
248
ossInit(const struct deviceinfo * card)249 static int ossInit(const struct deviceinfo *card)
250 {
251 memcpy(¤tcard, card, sizeof(struct deviceinfo));
252
253 igain=(int8_t)(card->opt>>8);
254
255 smpSetOptions=SetOptions;
256 smpSample=ossSample;
257 smpStop=ossStop;
258 smpSetSource=ossSetSource;
259
260 if (!card->mixer[0])
261 fd_mixer=-1;
262 else if ((fd_mixer=open(card->mixer, O_RDWR|O_NONBLOCK))<0)
263 {
264 #ifdef OSS_DEBUG
265 fprintf(stderr, "devsoss: open(%s, O_RDWR|O_NONBLOCK): %s\n", card->mixer, strerror(errno));
266 sleep(3);
267 #endif
268 } else {
269 if (fcntl(fd_mixer, F_SETFD, FD_CLOEXEC))
270 perror("fcntl(fd_mixer, F_SETFD, FD_CLOEXEC)");
271 }
272
273 smpSetOptions(44100, SMP_STEREO|SMP_16BIT);
274 smpSetSource(SMP_LINEIN);
275
276 return 1;
277 }
278
ossClose(void)279 static void ossClose(void)
280 {
281 smpSample=0;
282 smpStop=0;
283 smpSetOptions=0;
284 smpSetSource=0;
285 if (fd_dsp>=0)
286 close(fd_dsp);
287 fd_dsp=-1;
288 if (fd_mixer>=0)
289 close(fd_mixer);
290 fd_mixer=-1;
291 }
292
ossDetect(struct deviceinfo * card)293 static int ossDetect(struct deviceinfo *card)
294 {
295 struct stat st;
296 #if defined(OSS_GETVERSION)
297 int tmp;
298 #endif
299 char *temp;
300
301 card->devtype=&smpOSS;
302 card->port=-1;
303 card->port2=-1;
304 card->subtype=-1;
305 card->mem=0;
306 if ((card->chan<=0)||(card->chan>2))
307 card->chan=2;
308 if ((temp=getenv("DSP")))
309 {
310 #ifdef OSS_DEBUG
311 fprintf(stderr, "devsoss: $DSP found\n");
312 #endif
313 strncpy(card->path, temp, DEVICE_NAME_MAX);
314 card->path[DEVICE_NAME_MAX-1]=0;
315 } else if (!card->path[0])
316 {
317 #ifdef OSS_DEBUG
318 fprintf(stderr, "devsoss: path not set, using /dev/dsp\n");
319 #endif
320 strcpy(card->path, "/dev/dsp");
321 }
322 if ((temp=getenv("MIXER")))
323 {
324 #ifdef OSS_DEBUG
325 fprintf(stderr, "devsoss: $MIXER found\n");
326 #endif
327 strncpy(card->mixer, temp, DEVICE_NAME_MAX);
328 card->mixer[DEVICE_NAME_MAX-1]=0;
329 }
330 if (stat(card->path, &st))
331 {
332 #ifdef OSS_DEBUG
333 fprintf(stderr, "devsoss: stat(%s, &st): %s\n", card->path, strerror(errno));
334 #endif
335 return 0;
336 }
337
338 /* test if basic OSS functions exists */
339 if ((fd_dsp=open(card->path, O_RDONLY|O_NONBLOCK))<0)
340 {
341 if (errno==EAGAIN)
342 {
343 #ifdef OSS_DEBUG
344 fprintf(stderr, "devsoss: OSS detected (EGAIN)\n");
345 #endif
346 return 1;
347 }
348 #ifdef OSS_DEBUG
349 fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", card->path, strerror(errno));
350 #endif
351 return 0;
352 }
353
354 #if defined(OSS_GETVERSION)
355 if (ioctl(fd_dsp, OSS_GETVERSION, &tmp)<0)
356 {
357 #ifdef OSS_DEBUG
358 perror("devposs: (warning) ioctl(fd_dsp, OSS_GETVERSION, &tmp)");
359 #endif
360 }
361 #endif
362
363 #ifdef OSS_DEBUG
364 fprintf(stderr, "devposs: OSS detected\n");
365 #endif
366 close(fd_dsp);
367 fd_dsp=-1;
368
369 return 1; /* device exists, so we are happy.. can do better tests later */
370 }
371
ossGetOpt(const char * sec)372 static uint32_t ossGetOpt(const char *sec)
373 {
374 uint32_t opt=0;
375 int8_t igain;
376
377 if (cfGetProfileBool(sec, "revstereo", 0, 0))
378 opt|=REVSTEREO;
379 igain=cfGetProfileInt(sec, "igain", -1, 10);
380 opt|=(((uint16_t)igain)<<8);
381 return opt;
382 }
383
384 struct devaddstruct smpOSSAdd = {ossGetOpt, 0, 0, 0};
385 struct sounddevice smpOSS={SS_SAMPLER, 0, "OSS Recorder", ossDetect, ossInit, ossClose, &smpOSSAdd};
386
387 char *dllinfo="driver smpOSS";
388 struct linkinfostruct dllextinfo = {.name = "devsoss", .desc = "OpenCP Sampler Device: OSS (c) 2004-09 Stian Skjelstad", .ver = DLLVERSION, .size = 0};
389