1 /*
2 * audio-freebsd.cc --
3 *
4 * Old audio driver for FreeBSD 2 and 3. This has been superseded by
5 * audio-oss.cc.
6 */
7
8 /*
9 * Full Duplex audio module for the new sound driver and full duplex
10 * cards. Luigi Rizzo, from original sources supplied by Amancio Hasty.
11 *
12 * This includes some enhancements:
13 * - limit the maximum size of the playout queue to approx 4 frames;
14 * this is necessary if the write channel is slower than expected;
15 * the fix is based on two new ioctls, AIOGCAP and AIONWRITE,
16 * but the code should compile with the old driver as well.
17 * - use whatever format is available from the card (included split
18 * format e.g. for the sb16);
19 */
20
21
22 #include <fcntl.h>
23 #include <machine/soundcard.h>
24 #include "audio.h"
25 #include "mulaw.h"
26 #include "tclcl.h"
27
28 #define ULAW_ZERO 0x7f
29
30 class FreeBSDAudio : public Audio {
31 public:
32 FreeBSDAudio();
33 virtual int FrameReady();
34 virtual u_char* Read();
35 virtual void Write(u_char *);
36 virtual void SetRGain(int);
37 virtual void SetPGain(int);
38 virtual void OutputPort(int);
39 virtual void InputPort(int);
40 virtual void Obtain();
41 virtual void Release();
42 /* virtual int HalfDuplex() const; */
43 protected:
44
45 u_char* readbuf;
46 u_short *s16_buf;
47
48 int mixerfd;
49
50 #if defined(AIOGCAP) /* new sound driver */
51 int play_fmt, rec_fmt ; /* the sb16 has split format... */
52 snd_capabilities soundcaps;
53 #endif
54 };
55
56 static class FreeBSDAudioClass : public TclClass {
57 public:
58 FreeBSDAudioClass() : TclClass("Audio/FreeBSD") {}
59 TclObject* create(int, const char*const*) {
60 return (new FreeBSDAudio);
61
62 }
63 } freebsd_audio_class;
64
65 FreeBSDAudio::FreeBSDAudio() : mixerfd(-1)
66 {
67 readbuf = new u_char[blksize_];
68 s16_buf = new u_short[blksize_];
69
70 memset(readbuf, ULAW_ZERO, blksize_);
71
72 /*
73 * The only way to determine if the device is full duplex
74 * or not is by actually opening it. Unfortunately, we might
75 * not be able to open it because some other process is
76 * using it. Assume half-duplex. Obtain() will override
77 * if appropriate.
78 */
79 duplex_ = 0;
80
81 input_names_ = "mike linein";
82 output_names_ = "speaker lineout";
83 }
84
85 void
86 FreeBSDAudio::Obtain()
87 {
88 char *thedev;
89 char buf[64];
90 int d = -1;
91
92 if (haveaudio())
93 abort();
94 thedev=getenv("AUDIODEV");
95 if (thedev==NULL)
96 thedev="/dev/audio";
97 else if (thedev[0]>='') {
98 d = atoi(thedev);
99 sprintf(buf,"/dev/audio%d", d);
100 thedev = buf ;
101 }
102 fd_ = open(thedev, O_RDWR );
103 thedev=getenv("MIXERDEV");
104 if (thedev == NULL)
105 if (d < 0)
106 thedev = "/dev/mixer";
107 else {
108 sprintf(buf,"/dev/mixer%d", d);
109 thedev = buf ;
110 }
111
112 mixerfd = open(thedev, O_RDWR);
113 if (fd_ >= 0) {
114 snd_chan_param pa;
115 struct snd_size sz;
116
117 ioctl(fd_, AIOGCAP, &soundcaps);
118
119 // ErikM
120 printf("FreeBSDAudio::Obtain: soundcaps.\n");
121 printf("\tleft=%d\n\tright=%d\n", soundcaps.left, soundcaps.right);
122 printf("\tformats=0x%lx\n", soundcaps.formats);
123 printf("\tinputs=0x%lx\n", soundcaps.inputs);
124 printf("\tmixers=0x%lx\n", soundcaps.mixers);
125 printf("\trate_min=%ld\n", soundcaps.rate_min);
126 printf("\trate_max=%ld\n", soundcaps.rate_max);
127
128 pa.play_rate = pa.rec_rate = 8000 ;
129 pa.play_format = pa.rec_format = AFMT_MU_LAW ;
130 switch (soundcaps.formats & (AFMT_FULLDUPLEX | AFMT_WEIRD)) {
131 case AFMT_FULLDUPLEX :
132
133 // ErikM
134 printf("FreeBSDAudio::Obtain: in AFMT_FULLDUPLEX\n");
135
136 /*
137 * this entry for cards with decent full duplex. Use s16
138 * preferably (some are broken in ulaw) or ulaw or u8 otherwise.
139 */
140 if (soundcaps.formats & AFMT_S16_LE)
141 pa.play_format = pa.rec_format = AFMT_S16_LE ;
142 else if (soundcaps.formats & AFMT_MU_LAW)
143 pa.play_format = pa.rec_format = AFMT_MU_LAW ;
144 else if (soundcaps.formats & AFMT_U8)
145 pa.play_format = pa.rec_format = AFMT_U8 ;
146 else {
147 printf("sorry, no supported formats\n");
148 close(fd_);
149 fd_ = -1 ;
150 return;
151 }
152 break ;
153 case AFMT_FULLDUPLEX | AFMT_WEIRD :
154
155 // ErikM
156 printf("FreeBSDAudio::Obtain: in AFMT_FULLDUPLEX | AFMT_WEIRD\n");
157
158 /* this is the sb16... */
159 if (soundcaps.formats & AFMT_S16_LE) {
160 pa.play_format = AFMT_S8 ;
161 pa.rec_format = AFMT_S16_LE;
162 } else {
163 printf("sorry, no supported formats\n");
164 close(fd_);
165 fd_ = -1 ;
166 return;
167 }
168 break ;
169 default :
170 #if 0
171 printf("sorry don't know how to deal with this card\n");
172 close (fd_);
173 fd_ = -1;
174 #endif
175 break;
176 }
177
178 // ErikM
179 printf("FreeBSDAudio::Obtain: pa.play_format=0x%lx, pa.rec_format=0x%lx\n",
180 pa.play_format, pa.rec_format);
181
182
183 ioctl(fd_, AIOSFMT, &pa);
184 play_fmt = pa.play_format ;
185 rec_fmt = pa.rec_format ;
186 sz.play_size = (play_fmt == AFMT_S16_LE) ? 2*blksize_ : blksize_;
187 sz.rec_size = (rec_fmt == AFMT_S16_LE) ? 2*blksize_ : blksize_;
188 ioctl(fd_, AIOSSIZE, &sz);
189
190 /*
191 * note: this uses a modified function of the new driver,
192 * which will return AFMT_FULLDUPLEX set in SNDCTL_DSP_GETFMTS
193 * for full-duplex devices. In the old driver this was 0 so
194 * the default is to use half-duplex for them. Note also that I have
195 * not tested half-duplex operation.
196 */
197 int i;
198 ioctl(fd_, SNDCTL_DSP_GETFMTS, &i);
199 // ErikM
200 #if 1
201 printf("SNDCTL_DSP_GETFMTS returns 0x%08x %s duplex\n",
202 i, i & AFMT_FULLDUPLEX ? "full":"half");
203 #endif
204 duplex_ = (i & AFMT_FULLDUPLEX) ? 1 : 0 ;
205
206 // ErikM
207 printf("FreeBSDAudio::Obtain: duplex_ = %d\n", duplex_);
208
209
210 /*
211 * Set the line input level to 0 to shut
212 * off the analog side-tone gain between
213 * the line-in and line-out. This would otherwise
214 * wreak havoc on an echo canceler, for example,
215 * plugged into the audio adaptor.
216 */
217 int v = 0;
218 (void)ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_LINE), &v);
219
220 /*
221 * Restore the hardware settings in case
222 * some other vat changed them.
223 */
224 InputPort(iport_);
225 SetRGain(rgain_);
226 SetPGain(pgain_);
227
228 if (duplex_)
229 Audio::Obtain();
230 else
231 notify();
232 }
233 }
234
235
236 void FreeBSDAudio::Release()
237 {
238 if (haveaudio()) {
239 if (mixerfd > 0) {
240 close(mixerfd);
241 }
242 mixerfd = -1;
243 Audio::Release();
244 }
245 }
246
247 void FreeBSDAudio::Write(u_char *cp)
248 {
249 long i = blksize_, l;
250
251 #if 0 && defined(AIOGCAP)
252 int queued;
253 #endif
254
255 if (play_fmt == AFMT_S16_LE) {
256 for (i=0; i< (long) blksize_; i++)
257 s16_buf[i] = mulawtolin[cp[i]] ;
258 cp = (u_char *)s16_buf;
259 i = 2 *blksize_ ;
260 }
261 else if (play_fmt == AFMT_S8) {
262 for (i=0; i< (long) blksize_; i++) {
263 int x = mulawtolin[cp[i]] ;
264 x = (x >> 8 ) & 0xff;
265 cp[i] = (u_char)x ;
266 }
267 i = blksize_ ;
268 } else if (play_fmt == AFMT_U8) {
269 for (i=0; i< (long) blksize_; i++) {
270 int x = mulawtolin[cp[i]] ;
271 x = (x >> 8 ) & 0xff;
272 x = (x ^ 0x80) & 0xff ;
273 cp[i] = (u_char)x ;
274 }
275 i = blksize_ ;
276 }
277 #if 0 && defined(AIOGCAP)
278 ioctl(fd_, AIONWRITE, &queued);
279 queued = soundcaps.bufsize - queued ;
280 if (play_fmt == AFMT_S16_LE) {
281 if (queued > 8*blksize_)
282 i -= 8 ;
283 } else {
284 if (queued > 4*blksize_)
285 i -= 4 ;
286 }
287 #endif
288 for ( ; i > 0 ; i -= l) {
289 l = write(fd_, cp, i);
290 cp += l;
291 }
292 }
293
294 u_char* FreeBSDAudio::Read()
295 {
296 u_char* cp;
297 long l=0, l0 = blksize_, i = blksize_;
298
299 cp = readbuf;
300
301 if (rec_fmt == AFMT_S16_LE) {
302 cp = (u_char *)s16_buf;
303 l0 = i = 2 *blksize_ ;
304 }
305 for ( ; i > 0 ; i -= l ) {
306 l = read(fd_, cp, i);
307 cp += l ;
308 }
309 if (rec_fmt == AFMT_S16_LE) {
310 for (i=0; i< (long) blksize_; i++)
311 readbuf[i] = lintomulaw[ s16_buf[i] & 0xffff ] ;
312 }
313 else if (rec_fmt == AFMT_S8) {
314 for (i=0; i< (long) blksize_; i++)
315 readbuf[i] = lintomulaw[ readbuf[i]<<8 ] ;
316 }
317 else if (rec_fmt == AFMT_U8) {
318 for (i=0; i< (long) blksize_; i++)
319 readbuf[i] = lintomulaw[ (readbuf[i]<<8) ^ 0x8000 ] ;
320 }
321 return readbuf;
322 }
323
324 /*
325 * should check that I HaveAudio() before trying to set gain.
326 *
327 * In most mixer devices, there is only a master volume control on
328 * the capture channel, so the following code does not really work
329 * as expected. The only (partial) exception is the MIC line, where
330 * there is generally a 20dB boost which can be enabled or not
331 * depending on the type of device.
332 */
333 void FreeBSDAudio::SetRGain(int level)
334 {
335 rgain_ = level;
336 float x = level;
337 level = (int) (x/2.56);
338 int foo = (level<<8) | level;
339 switch (iport_) {
340 case 2:
341 if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
342 perror("failed set input line volume");
343 break;
344 case 1:
345 if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
346 perror("failed set input line volume");
347 break;
348 case 0:
349 if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_MIC), &foo) == -1)
350 perror("failed to set mic volume");
351 if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_IGAIN), &foo) == -1)
352 perror("failed set input line volume");
353 break;
354 }
355 }
356
357 void FreeBSDAudio::SetPGain(int level)
358 {
359 pgain_ = level;
360 float x = level;
361 level = (int) (x/2.56);
362 int foo = (level<<8) | level;
363 if (mixerfd >= 0) {
364 if (ioctl(mixerfd, MIXER_WRITE(SOUND_MIXER_PCM), &foo) == -1) {
365 perror("failed to output level");
366 }
367 }
368 }
369
370 void FreeBSDAudio::OutputPort(int p)
371 {
372 oport_ = p;
373 }
374
375 void FreeBSDAudio::InputPort(int p)
376 {
377 int zero = 0;
378
379 switch(p) {
380 case 2:
381 zero = 1 << SOUND_MIXER_CD;
382 break;
383 case 1:
384 zero = 1 << SOUND_MIXER_LINE;
385 break;
386 case 0 :
387 zero = 1 << SOUND_MIXER_MIC;
388 break;
389 }
390 if ( ioctl(mixerfd, SOUND_MIXER_WRITE_RECSRC, &zero) == -1 ) {
391 perror("failed to select input");
392 p = 0;
393 }
394 iport_ = p;
395 }
396
397 /*
398 * FrameReady must return 0 every so often, or the system will keep
399 * processing mike data and not other events.
400 */
401 int FreeBSDAudio::FrameReady()
402 {
403 int i = 0;
404 int lim = blksize_;
405
406 ioctl(fd_, FIONREAD, &i );
407 if (rec_fmt == AFMT_S16_LE) lim = 2*blksize_;
408 return (i >= lim) ? 1 : 0 ;
409 }
410
411 /*** end of file ***/
412
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.