From: MERC::"uunet!CRVAX.SRI.COM!RELAY-INFO-VAX" 23-JUL-1992 00:01:00.49 To: info-vax@kl.sri.com CC: Subj: Re: VAXstation 4000 sound port ?? (again) In article <1992Jul21.202152.46@roads.sa.gov.au> walsh@roads.sa.gov.au writes: Hi, does anyone know how to produce sound out of the headphone/telephone socket of a VAXstation VLC or model 60 ? ... Also, a file we sourced from a.b.s.misc and UUDECODEd gave us an error something like `record size too big' when we tried to copy it to the device mentioned below. Is that due to the file type created by the 'C' version of UUDECODE that we have ? If the record type of the file was stream_lf, then you'll probably run into record length problems trying to use COPY to send it to the SO device (.au files often don't have many 0x0a's in them). One thing you could do is convert the file to fixed length records. You'll probably need to write a program to do this; I don't think you can use CONVERT. (bilf from the zoo distribution would probably work; i also have a program which will do this, but it's fairly trivial to write.) However, I typically use a custom program to do the copy (which I'm appending below). This is for several reasons: - It can read large stream_lf files directly, as well as other file types. - If you copy a fixed-length file with a small record size to SO, you'll probably notice a distracting, rapid clicking. SODRIVER seems to generate a click between each I/O (when it resets the AMD chip? or maybe the next I/O can't get started fast enough?). The only work-around I've been able to find is to make the individual I/Os as large as possible, so that the clicks become relatively infrequent. (I'd love to hear of a way to get rid of the clicks entriely...) With my own program, I can control the size of the I/Os to the SO device, independently of the attributes of the source file. - COPY doesn't do enough buffering to prevent audible glitches in many cases. For example, I tried copying a file from 8mm tape to SO. There were extremely noticeable glitches in the sound in sync with the data transfers from the tape. I try to keep several I/Os queued to the driver, both for the buffering and to attempt to reduce the turnaround time between I/Os. WARNING: In VMS 5.5, SODRIVER has several bugs, the most serious of which can permanently hang a process during image exit: - The I/O completion code for write processing replaces IRP$W_BCNT with the number of bytes actually transferred. Now, the driver locks the I/O buffer at the start of the operation with exe$writelock, and the QIO postprocessing code uses IRP$W_BCNT to figure out which pages to unlock. The upshot is that if an I/O does not complete, some of the buffer pages will not be unlocked. When the image exits, the delete-page code sees that there are still locked pages, assumes that there is still an I/O in progress, and places the process in PFW until it completes. Of course, the I/O has already finished, so the process hangs forever. If you have source listings, you should be able to repair this fairly easily. (If anyone's interested, I have some emacs code which will do most of the work in converting macro/C/bliss listings back into compilable source...) - The byte count for the transfer is of course 16 bits wide. Testing for the end of the transfer is done with a _signed_ _word_comparison against 0. Thus, any operations with a byte count >= 32768 complete immediately, without transferring any data (and will provoke the previous bug). - In any event, I/Os usually seem to stop after transferring ~21k-22k of data. I haven't tracked this down yet; I'm wondering if something is timing out... As an extra treat, here's a summary of the SODRIVER QIOs, gleaned from the source listing. I haven't dug into the input code, so don't ask me about that... The hardware is based on the AMD 79C30A chip; anyone have a summary of its functions? IO$_WRITEVBLK: p1=buffer, p2=buflen, p3=timeout (units of 10ms) I don't think the timeout parameter acually does anything. IO$_READVBLK: p1=buffer, p2=buflen I haven't gotten this to work yet. I think that reading has to first be enabled with one of the other calls. IO$_CANCEL: IO$_ACCESS: p3=code, p4=first parameter, p5=second parameter p3=0: no access (turn off AMD chip?) p3=1: ringing tone (p3=frequency (Hz), p4=amplitude (0-100%)) p3=2: single frequency tone (p3=frequency (Hz), p4=amplitude (0-100%)) p3=3: DTMF tones (p3 [low word]=first freq (Hz), [high word]=second freq; p4 [low word]=first amplitude (0-100%), [high word]=second) p3=4: play single note (p3=note index, p4=amplitude (0-100%)) p3=5: play ringing note (p3=note index, p4=amplitude (0-100%)) p3=6: play DTMF note (p3=notes, p4=amplitudes) p3=7: readstart (p3=record level (0-100)) (i'm not sure how this works...) if C3 is middle C, note indices range from 0=B2 to B5, by half-steps. IO$_ACPCONTROL: p1=buffer, p2=buflen, p3=code, p4=regno p3=1: setqsize p3=2: getqsize p3=3: getreg p3=4: setreg p3=5: readstart p3=6: stop p3=7: pause p3=8: resume IO$_SETMODE: p1=characteristics scott snyder snyder@d0sb10.fnal.gov ----------------------- play.c ----------------------------- #include #include #include #include #define BSIZ 16385 #define MAX_IOS 4 volatile int n_ios = 0; volatile int io_in_progress[MAX_IOS]; volatile struct { short stat, len; int dum; } iosb[MAX_IOS]; void io_end (int bufi) { --n_ios; io_in_progress[bufi] = 0; } int readbuf (int fd, char *buf, int blen) { int n_read = 0, n; while (blen > 0 && (n = read (fd, buf, blen)) > 0) { if (n < blen && buf[n-1] == '\n') --n; n_read += n; buf += n; blen -= n; } return n_read; } main (int argc, char **argv) { int fd, n, stat, efn, bufi; short chan; $DESCRIPTOR (dev_dsc, "soa0:"); char buf[MAX_IOS][BSIZ]; if (argc != 2) { fprintf (stderr, "usage: play \n"); exit(1); } fd = open (argv[1], O_RDONLY, 0); if (fd < 0) { perror ("opening input"); exit(1); } stat = sys$assign (&dev_dsc, &chan, 0, 0); if ((stat&1) == 0) lib$signal (stat); stat = lib$get_ef (&efn); if ((stat&1) == 0) lib$signal (stat); bufi = 0; while ((n = readbuf (fd, buf[bufi], sizeof (buf[bufi]))) > 0) { printf ("%d %d\n", n_ios, n); fflush(stdout); printf ("iosb %d %d\n", iosb[bufi].stat, iosb[bufi].len); ++n_ios; io_in_progress[bufi] = 1; stat = sys$qio (efn, chan, IO$_WRITEVBLK, &iosb[bufi], io_end, bufi, buf[bufi], n, 0, 0, 0, 0); if ((stat&1) == 0) lib$signal (stat); while (n_ios == MAX_IOS) { stat = sys$waitfr (efn); if ((stat&1) == 0) lib$signal (stat); stat = sys$clref (efn); if ((stat&1) == 0) lib$signal (stat); } for (bufi=0; bufi= MAX_IOS) abort(); } /* wait for all I/O's to complete! */ while (n_ios > 0) { printf ("%d\n", n_ios); stat = sys$waitfr (efn); if ((stat&1) == 0) lib$signal (stat); stat = sys$clref (efn); if ((stat&1) == 0) lib$signal (stat); } for (bufi=0; bufi