/* * Copyright (c) 1991-1994 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char rcsid[] = "@(#) $Header: audio.cc,v 1.34 96/05/03 06:26:49 van Exp $ (LBL)"; #include #include #include #if defined(sgi) #include #endif #ifdef WIN32 #include #else #include #include #include #endif #include "audio.h" #include "filter.h" #include "mulaw.h" #include "Tcl.h" Audio::Audio() : lock_fd(-1), blksize(FRAMESIZE), fd(-1), oport(0), iport(0), oports(1), iports(1), rmute(0), pmute(0), loopback(0), sampleclk(0), rgain(0), pgain(0), filter(new Filter(this)), handler_(0) { ext_fname[0]='\0'; for (u_int i = 0; i < sizeof(omode)/sizeof(omode[0]); ++i) omode[i] = mode_mikemutesnet; } Audio::~Audio() { close(fd); delete filter; } void Audio::Release() { if (HaveAudio()) { unlink(); (void)close(fd); fd = -1; notify(); } } void Audio::Obtain() { link(fd, TK_READABLE); notify(); } void Audio::InputPort(int p) { iport = p; } void Audio::OutputPort(int p) { oport = p; } void Audio::dispatch(int) { while (FrameReady()) handler_->audio_handle(); } /* * The following are called by user interface routines to get * the input/output ports & port types. */ int Audio::inputs() const { return (iports); } int Audio::input_type(int i) const { return (i < inputs()? i : 0); /*XXX*/ } int Audio::outputs() const { return (oports); } int Audio::output_type(int i) const { return (i < outputs()? i : 0); /*XXX*/ } int Audio::StrToMode(const char *s) const { if (strcasecmp("MikeMutesNet", s) == 0 || strcasecmp("SpeakerPhone", s) == 0) return mode_mikemutesnet; else if (strcasecmp("NetMutesMike", s) == 0 || strcasecmp("VTSpeakerPhone", s) == 0) return mode_netmutesmike; else if (strcasecmp("EchoCancel", s) == 0 || strcasecmp("ec", s) == 0) return mode_ec; else return mode_none; } int Audio::StrToIPort(const char *s) const { if ( strcasecmp("mike", s) == 0 || strcasecmp("microphone", s) == 0 || strcasecmp("mic", s) == 0 || strcasecmp("wave", s) == 0 || strcasecmp("wavein", s) == 0 || strcasecmp("wave in", s) == 0 ) return input_mike; else if ( strcasecmp("linein", s) == 0 || strcasecmp("line-in", s) == 0 || strcasecmp("line in", s) == 0 || strcasecmp("line", s) == 0 || strcasecmp("linein1", s) == 0 || strcasecmp("line-in1", s) == 0 || strcasecmp("line-in 1", s) == 0 || strcasecmp("line in 1", s) == 0 ) return input_line; else if ( strcasecmp("linein2", s) == 0 || strcasecmp("line-in2", s) == 0 || strcasecmp("line-in 2", s) == 0 || strcasecmp("line in 2", s) == 0 || strcasecmp("line2", s) == 0 || strcasecmp("aux", s) == 0 || strcasecmp("cd", s) == 0 || strcasecmp("cd audio", s) == 0 ) return input_line2; else if ( strcasecmp("linein3", s) == 0 || strcasecmp("line-in3", s) == 0 || strcasecmp("line-in 3", s) == 0 || strcasecmp("line in 3", s) == 0 || strcasecmp("line3", s) == 0 || strcasecmp("synth", s) == 0 || strcasecmp("synthesizer", s) == 0 || strcasecmp("fm", s) == 0 || strcasecmp("wt", s) == 0 || strcasecmp("wavetable", s) == 0 ) return input_line3; else return -1; } int Audio::StrToOPort(const char *s) const { if ( strcasecmp("speaker", s) == 0 || strcasecmp("wave", s) == 0 || strcasecmp("waveout", s) == 0 || strcasecmp("wave out", s) == 0 ) return output_speaker; else if ( strcasecmp("jack", s) == 0 || strcasecmp("phones", s) == 0 || strcasecmp("headphone", s) == 0 || strcasecmp("headphones", s) == 0 ) return output_phones; else if ( strcasecmp("lineout", s) == 0 || strcasecmp("line out", s) == 0 || strcasecmp("line", s) == 0 || strcasecmp("lineout1", s) == 0 || strcasecmp("line out1", s) == 0 || strcasecmp("line1", s) == 0 ) return output_line; else if ( strcasecmp("lineout2", s) == 0 || strcasecmp("line out2", s) == 0 || strcasecmp("line out 2", s) == 0 || strcasecmp("line2", s) == 0 || strcasecmp("aux", s) == 0 || strcasecmp("aux out", s) == 0 ) return output_line2; else return -1; } const char* Audio::IPortToStr(int p) const { switch (p) { default: case input_mike: return ("mike"); case input_line: return ("linein"); case input_line2: return ("linein2"); case input_line3: return ("linein3"); } } const char* Audio::OPortToStr(int p) const { switch (p) { default: case output_speaker: return ("speaker"); case output_phones: return ("jack"); case output_line: return ("lineout"); case output_line2: return ("lineout2"); } } /* XXX this can be coded better */ /* * Output the ulaw sample in x and read the mike response back into y. * The two signals are guaranteed to coincide in time. len must be * less than the max output buffer available (currently 4K). */ int Audio::PlayRec(u_char *x, u_char *y, int len) { int bufsize = (len / blksize) * blksize; int rbufsize = bufsize + 4096; u_char* tmpbuf = new u_char[rbufsize]; Flush(); int cc = write(fd, (char*)x, bufsize); if (cc != bufsize) { perror("PlayRec write"); return (bufsize); } int offset = AdjustTime(0); if (offset < 0 || offset + bufsize > rbufsize) { fprintf(stderr, "Playrec offset %d\n", offset); return (bufsize); } char* bp = (char*)tmpbuf; for (int rem = offset + bufsize; rem > 0; ) { if ((cc = read(fd, bp, rem)) <= 0) { perror("PlayRec read"); return (bufsize); } bp += cc; rem -= cc; } memcpy((char*)y, (char*)&tmpbuf[offset], bufsize); delete tmpbuf; return (bufsize); } void Audio::RetrainFilter(int len, int maxtaps) { filter->Train(len, maxtaps); } void Audio::RMute() { rmute |= 1; } void Audio::RUnmute() { rmute &=~ 1; } void Audio::SetRGain(int) { } void Audio::SetPGain(int) { } #if defined(__osf__) || defined(sun) || defined(ultrix) extern "C" { int flock(int, int); } #endif #if defined(hpux) || defined(__svr4__) || defined(sco) #include #define LOCK_SH 1 /* shared lock */ #define LOCK_EX 2 /* exclusive lock */ #define LOCK_NB 4 /* don't block when locking */ #define LOCK_UN 8 /* unlock */ int flock(int fd, int op) { struct flock f; f.l_whence = 0; f.l_start = 0; f.l_len = 0; if (op == LOCK_UN) f.l_type = F_UNLCK; else f.l_type = F_WRLCK; return (fcntl(fd, F_SETLK, &f)); } #endif /* XXX should make a NOLOCKING define that configure sets */ #ifdef WIN32 void Audio::openlock() { printf("Audio:openlock\n"); } void Audio::unlock() { printf("Audio:unlock\n"); } int Audio::lock() { printf("Audio:lock\n"); return (0); } #else void Audio::openlock() { char *wrk = new char[sizeof("/tmp/.vat_audio_lock.") + 32]; sprintf(wrk, "/tmp/.vat_audio_lock.%d", getuid()); /* open (or create) the lock file */ lock_fd = open(wrk, O_RDWR|O_CREAT, 0777); if (lock_fd < 0) { perror(wrk); exit(2); } delete wrk; } void Audio::unlock() { if (flock(lock_fd, LOCK_UN)) perror("vat: sock_audio unlock"); } int Audio::lock() { return (flock(lock_fd, (LOCK_EX|LOCK_NB))); } #endif int Audio::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 4) { if (strcmp(argv[1], "mode") == 0) { int port = StrToOPort(argv[2]); int mode = StrToMode(argv[3]); SetMode(port, mode); return (TCL_OK); } } if (argc == 2) { if (strcmp(argv[1], "halfduplex") == 0) { tcl.result(HalfDuplex() ? "1" : "0"); return (TCL_OK); } if (strcmp(argv[1], "obtain") == 0) { Obtain(); return (TCL_OK); } if (strcmp(argv[1], "release") == 0) { Release(); return (TCL_OK); } if (strcmp(argv[1], "have") == 0) { tcl.result(HaveAudio() ? "1" : "0"); return (TCL_OK); } } else if (argc == 3) { if (strcmp(argv[1], "test") == 0) { SetLoopback(atoi(argv[2])); return (TCL_OK); } if (strcmp(argv[1], "input") == 0) { if (strcmp(argv[2], "mute") == 0) { RMute(); return (TCL_OK); } if (strcmp(argv[2], "unmute") == 0) { RUnmute(); tcl.result(HaveAudio()? "0" : "1"); return (TCL_OK); } if (strcmp(argv[2], "current") == 0) { sprintf(tcl.result(), "%d", InputPort()); return (TCL_OK); } if (strcmp(argv[2], "names") == 0) { /* * Return a list of the names of the * audio input ports. */ char* cp = tcl.result(); int n = inputs(); for (int i = 0; i < n; ++i) { const char* port = IPortToStr(i); strcpy(cp, port); cp += strlen(port); *cp++ = ' '; } *cp++ = '\0'; return (TCL_OK); } if (strcmp(argv[2], "gain") == 0) { sprintf(tcl.result(), "%s %d", IPortToStr(InputPort()), RGain()); return (TCL_OK); } } else if (strcmp(argv[1], "output") == 0) { if (strcmp(argv[2], "gain") == 0) { sprintf(tcl.result(), "%s %d", OPortToStr(OutputPort()), PGain()); return (TCL_OK); } if (strcmp(argv[2], "mute") == 0) { PMute(); return (TCL_OK); } if (strcmp(argv[2], "unmute") == 0) { PUnmute(); return (TCL_OK); } if (strcmp(argv[2], "current") == 0) { sprintf(tcl.result(), "%d", OutputPort()); return (TCL_OK); } if (strcmp(argv[2], "names") == 0) { /* * Return a list of the names of the * audio output ports. */ char* cp = tcl.result(); int n = outputs(); for (int i = 0; i < n; ++i) { const char* port = OPortToStr(i); strcpy(cp, port); cp += strlen(port); *cp++ = ' '; } *cp++ = '\0'; return (TCL_OK); } } else if (strcmp(argv[1], "filename") == 0) { strncpy(ext_fname, argv[2], sizeof(ext_fname)); InputPort(input_line3); return (TCL_OK); } } else if (argc == 4) { if (strcmp(argv[1], "input") == 0) { if (strcmp(argv[2], "gain") == 0) { SetRGain(atoi(argv[3])); return (TCL_OK); } if (strcmp(argv[2], "set") == 0) { InputPort(StrToIPort(argv[3])); return (TCL_OK); } if (strcmp(argv[2], "name") == 0) { strcpy(tcl.result(), IPortToStr(StrToIPort(argv[3]))); return (TCL_OK); } } if (strcmp(argv[1], "output") == 0) { if (strcmp(argv[2], "gain") == 0) { SetPGain(atoi(argv[3])); return (TCL_OK); } if (strcmp(argv[2], "set") == 0) { OutputPort(StrToOPort(argv[3])); return (TCL_OK); } if (strcmp(argv[2], "name") == 0) { strcpy(tcl.result(), OPortToStr(StrToOPort(argv[3]))); return (TCL_OK); } } } return (TclObject::command(argc, argv)); }