1 /*
2 * audio.cc --
3 *
4 * FIXME: This file needs a description here.
5 *
6 * Copyright (c) 1991-2002 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * A. Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * B. Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * C. Neither the names of the copyright holders nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
22 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 static const char rcsid[] =
35 "@(#) $Header: /usr/mash/src/repository/mash/mash-1/audio/audio.cc,v 1.19 2002/05/01 01:17:22 weitsang Exp $";
36
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #if defined(sgi)
41 #include <bstring.h>
42 #endif
43 #ifdef WIN32
44 #include <fcntl.h>
45 #else
46 #include <unistd.h>
47 #include <sys/file.h>
48 #include <fcntl.h>
49 #endif
50
51 #include "audio.h"
52 #include "mulaw.h"
53 #include "tclcl.h"
54
55
56 Audio::Audio() :
57 lock_fd_(-1),
58 blksize_(AUDIO_FRAMESIZE),
59 fd_(-1),
60 oport_(0),
61 iport_(0),
62 rmute_(0),
63 pmute_(0),
64 rgain_(0),
65 pgain_(0),
66 duplex_(1),
67 input_names_(0),
68 output_names_(0),
69 handler_(0),
70 device_(-1)
71 {
72 omode_ = mode_mikemutesnet;
73 bind("duplex_", &duplex_);
74 }
75
76 Audio::~Audio()
77 {
78 close(fd_);
79 }
80
81 void Audio::Release()
82 {
83 if (haveaudio()) {
84 unlink();
85 (void)close(fd_);
86 fd_ = -1;
87 notify();
88 }
89 }
90
91 void Audio::Obtain()
92 {
93 link(fd_, TCL_READABLE);
94 notify();
95 }
96
97 void Audio::InputPort(int p)
98 {
99 iport_ = p;
100 }
101
102 void Audio::OutputPort(int p)
103 {
104 oport_ = p;
105 }
106
107 void Audio::dispatch(int)
108 {
109 while (FrameReady()) {
110 if (handler_ != 0)
111 handler_->audio_handle();
112 }
113 }
114
115 /*
116 * Output the ulaw sample in x and read the mike response back into y.
117 * The two signals are guaranteed to coincide in time. len must be
118 * less than the max output buffer available (currently 4K).
119 */
120 int Audio::PlayRec(u_char *x, u_char *y, int len)
121 {
122 int bufsize = (len / blksize_) * blksize_;
123 int rbufsize = bufsize + 4096;
124 u_char* tmpbuf = new u_char[rbufsize];
125 Flush();
126 int cc = write(fd_, (char*)x, bufsize);
127 if (cc != bufsize) {
128 perror("PlayRec write");
129 delete[] tmpbuf;
130 return (bufsize);
131 }
132 int offset = AdjustTime(0);
133 if (offset < 0 || offset + bufsize > rbufsize) {
134 fprintf(stderr, "Playrec offset %d\n", offset);
135 delete[] tmpbuf;
136 return (bufsize);
137 }
138 char* bp = (char*)tmpbuf;
139 for (int rem = offset + bufsize; rem > 0; ) {
140 if ((cc = read(fd_, bp, rem)) <= 0) {
141 perror("PlayRec read");
142 delete[] tmpbuf;
143 return (bufsize);
144 }
145 bp += cc;
146 rem -= cc;
147 }
148 memcpy((char*)y, (char*)&tmpbuf[offset], bufsize);
149 delete[] tmpbuf;
150 return (bufsize);
151 }
152
153 void Audio::RMute()
154 {
155 rmute_ |= 1;
156 }
157
158 void Audio::RUnmute()
159 {
160 rmute_ &=~ 1;
161 }
162
163 void Audio::SetRGain(int)
164 {
165 }
166
167 void Audio::SetPGain(int)
168 {
169 }
170
171 #if defined(__osf__) || defined(sun) || defined(ultrix) || defined(sgi)
172 extern "C" {
173 int flock(int, int);
174 }
175 #endif
176 #if defined(hpux) || defined(__svr4__) || defined(sco) || defined(_AIX)
177 #include <fcntl.h>
178
179 #define LOCK_SH 1 /* shared lock */
180 #define LOCK_EX 2 /* exclusive lock */
181 #define LOCK_NB 4 /* don't block when locking */
182 #define LOCK_UN 8 /* unlock */
183
184 int flock(int fd, int op) {
185 struct flock f;
186 f.l_whence = 0;
187 f.l_start = 0;
188 f.l_len = 0;
189 if (op == LOCK_UN)
190 f.l_type = F_UNLCK;
191 else
192 f.l_type = F_WRLCK;
193 return (fcntl(fd, F_SETLK, &f));
194 }
195 #endif
196
197 /* FIXME should make a NOLOCKING define that configure sets */
198 #ifdef WIN32
199 void Audio::openlock() { printf("Audio:openlock\n"); }
200 void Audio::unlock() { printf("Audio:unlock\n"); }
201 int Audio::lock() { printf("Audio:lock\n"); return (0); }
202 #else
203 void Audio::openlock()
204 {
205 char *wrk = new char[sizeof("/tmp/.vat_audio_lock.") + 32];
206 sprintf(wrk, "/tmp/.vat_audio_lock.%d", (int)getuid());
207 /* open (or create) the lock file */
208 lock_fd_ = open(wrk, O_RDWR|O_CREAT, 0777);
209 if (lock_fd_ < 0) {
210 perror(wrk);
211 exit(2);
212 }
213 delete wrk;
214 }
215
216 void Audio::unlock()
217 {
218 if (::flock(lock_fd_, LOCK_UN))
219 perror("vat: sock_audio unlock");
220 }
221
222 int Audio::lock()
223 {
224 return (::flock(lock_fd_, (LOCK_EX|LOCK_NB)));
225 }
226 #endif
227
228 int Audio::SetAudioDevice(int dev)
229 {
230 device_ = dev;
231
232 return(1);
233 }
234
235 /*
236 * <otcl> Class Audio
237 * Audio is the
238 * base class for objects that represent audio codecs.
239 * An audio object is both a source and a sink of data and
240 * rather than spliced onto other objects in a pipeline,
241 * it is instanced inside of and manipulated principally by
242 * the AudioController object.
243 */
244 int Audio::command(int argc, const char*const* argv)
245 {
246 Tcl& tcl = Tcl::instance();
247 if (argc == 2) {
248 /*
249 * <otcl> Audio public get_input_ports {}
250 * Return the list of available input ports.
251 * The list consists of a sequence of names,
252 * where the name is the nick name of the port
253 * (e.g., "mike"). A ports position in this
254 * list identifies its integer port number,
255 * which must be used in many of the method
256 * calls that select and/or manipulate the port.
257 */
258 if (strcmp(argv[1], "get_input_ports") == 0) {
259 if (input_names_ == 0) {
260 tcl.result("");
261 } else {
262 tcl.result(input_names_);
263 }
264 return (TCL_OK);
265 }
266 /*
267 * <otcl> Audio public get_output_ports {}
268 * Return the list of available output ports.
269 * The list consists of a sequence of names,
270 * where the name is the nick name of the port
271 * (e.g., "speaker"). A ports position in this
272 * list identifies its integer port number,
273 * which must be used in many of the method
274 * calls that select and/or manipulate the port.
275 */
276 if (strcmp(argv[1], "get_output_ports") == 0) {
277 if (output_names_ == 0) {
278 tcl.result("");
279 } else {
280 tcl.result(output_names_);
281 }
282 return (TCL_OK);
283 }
284 /*
285 * <otcl> Audio public obtain {}
286 * Attempt to obtain the audio device. If successful,
287 * notify observers.
288 */
289 if (strcmp(argv[1], "obtain") == 0) {
290 if (haveaudio()) {
291 tcl.result("calling obtain when audio already held");
292 return (TCL_ERROR);
293 }
294 Obtain();
295 return (TCL_OK);
296 }
297 /*
298 * <otcl> Audio public release {}
299 * Release the audio device and notify observers.
300 * This allows some other Audio object running
301 * in either the same or separate process,
302 * to obtain the underlying audio device
303 * (since many audio services are not shared).
304 */
305 if (strcmp(argv[1], "release") == 0) {
306 Release();
307 return (TCL_OK);
308 }
309 /*
310 * <otcl> Audio public have {}
311 * Return 1 if the underlying audio device is currently open
312 * and ready, and 0 otherwise.
313 */
314 if (strcmp(argv[1], "have") == 0) {
315 tcl.result(haveaudio() ? "1" : "");
316 return (TCL_OK);
317 /*
318 * <otcl> Audio public get_input_port
319 * Return the input port number of the currently
320 * selected port. This number
321 * identifies the index of the port listed in the
322 * set of names returned by Audio::get_input_ports.
323 * </ul>
324 */
325 }
326 if (strcmp(argv[1], "get_input_port") == 0) {
327 sprintf(tcl.result(), "%d", InputPort());
328 return (TCL_OK);
329 }
330 /*
331 * <otcl> Audio public get_output_port
332 * Return the output port number of the currently
333 * selected port. This number
334 * identifies the index of the port listed in the
335 * set of names returned by Audio::get_output_ports.
336 * </ul>
337 */
338 if (strcmp(argv[1], "get_output_port") == 0) {
339 sprintf(tcl.result(), "%d", OutputPort());
340 return (TCL_OK);
341 }
342 } else if (argc == 3) {
343 /*
344 * <otcl> Audio public set_speakerphone mode
345 * Sets the speakerphone attribute as indicated
346 * by <i>mode</i>, which can be one of:
347 * <ul>
348 * <li> fullduplex,
349 * <li> mikemutesnet, or
350 * <li> netmutesmike.
351 * </ul>
352 * FIXME: this should be an AudioController method
353 */
354 if (strcmp(argv[1], "set_speakerphone") == 0) {
355 if (strcasecmp(argv[2], "mikemutesnet") == 0)
356 omode_ = mode_mikemutesnet;
357 else if (strcasecmp(argv[2], "netmutesmike") == 0)
358 omode_ = mode_netmutesmike;
359 else
360 omode_ = mode_none;
361
362 return (TCL_OK);
363 }
364
365 /*
366 * <otcl> Audio public set_input_gain level
367 * Set the input gain for the currently enabled input
368 * port to <i>level</i>, where level is a linear gain
369 * factor from 0 to 255. FIXME should change this?
370 * If the input port is changed or the device is
371 * released and re-obtained, the gain must
372 * be reset from OTcl to maintain a consistent
373 * and reliable level.
374 * </ul>
375 */
376 if (strcmp(argv[1], "set_input_gain") == 0) {
377 SetRGain(atoi(argv[2]));
378 return (TCL_OK);
379 }
380 /*
381 * <otcl> Audio public set_input_port portno
382 * Set the input port to <i>portno</i>, which
383 * identifies the index of the port listed in the
384 * set of names returned by Audio::get_input_ports.
385 * </ul>
386 */
387 if (strcmp(argv[1], "set_input_port") == 0) {
388 InputPort(atoi(argv[2]));
389 return (TCL_OK);
390 }
391 /*
392 * <otcl> Audio public set_input_mute val
393 * Set the mute attribute of the current port to <i>val</i>.
394 * If non-zero, the mike is muted and no input samples
395 * are generated and passed on to the controller;
396 * otherwise, the mike is enabled and becomes "live".
397 * </ul>
398 */
399 if (strcmp(argv[1], "set_input_mute") == 0) {
400 /*FIXME*/
401 if (atoi(argv[2]))
402 RMute();
403 else
404 RUnmute();
405 return (TCL_OK);
406 }
407 /*
408 * <otcl> Audio public set_ouptput_gain level
409 * Set the output gain for the currently enabled output
410 * port to <i>level</i>, where level is a linear gain
411 * factor from 0 to 255. FIXME should change this?
412 * If the output port is changed or the device is
413 * released and re-obtained, the gain must
414 * be reset from OTcl to maintain a consistent
415 * and reliable level.
416 * </ul>
417 */
418 if (strcmp(argv[1], "set_output_gain") == 0) {
419 SetPGain(atoi(argv[2]));
420 return (TCL_OK);
421 }
422 /*
423 * <otcl> Audio public set_output_port portno
424 * Set the input port to <i>portno</i>, which
425 * identifies the index of the port listed in the
426 * set of names returned by Audio::get_output_ports.
427 * </ul>
428 */
429 if (strcmp(argv[1], "set_output_port") == 0) {
430 OutputPort(atoi(argv[2]));
431 return (TCL_OK);
432 }
433 /*
434 * <otcl> Audio public set_output_mute val
435 * Set the mute attribute of the current port to <i>val</i>.
436 * If non-zero, the mike is muted and no output samples
437 * are generated and passed on to the controller;
438 * otherwise, the mike is enabled and becomes "live".
439 * </ul>
440 */
441 if (strcmp(argv[1], "set_output_mute") == 0) {
442 /*FIXME*/
443 if (atoi(argv[2]))
444 PMute();
445 else
446 PUnmute();
447 return (TCL_OK);
448 }
449
450 /*
451 * Call this to set which audio device will be used by the
452 * audio driver. 0 => /dev/audio0, /dev/mixer0, etc
453 */
454 if(strcmp(argv[1], "set_device") == 0)
455 {
456 SetAudioDevice(atoi(argv[2]));
457 return(TCL_OK);
458 }
459 }
460 return (TclObject::command(argc, argv));
461 }
462
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.