Date: Wed, 15 Aug 2001 17:15:19 -0400 From: The Anarcat <anarcat@anarcat.dyndns.org> To: Cameron Grant <gandalf@vilnya.demon.co.uk> Cc: freebsd-multimedia@freebsd.org Subject: Test record program triggers pcm problems on -stable (was: Re: kern/21438: Sox recording in 16 bits creates a panic: no feed) Message-ID: <20010815171514.B471@shall.anarcat.dyndns.org> In-Reply-To: <007301c0c6af$4d399650$0504020a@haveblue> References: <3ADB420A.B1B696A4@lmc.ericsson.se> <005901c0c6aa$4ec49560$0504020a@haveblue> <3ADB4835.E34532A5@lmc.ericsson.se> <007301c0c6af$4d399650$0504020a@haveblue>
next in thread | previous in thread | raw e-mail | index | archive | help
--ZJcv+A0YCCLh2VIg Content-Type: multipart/mixed; boundary="5G06lTa6Jq83wMTw" Content-Disposition: inline --5G06lTa6Jq83wMTw Content-Type: text/plain; charset=us-ascii Content-Description: Mail text Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi Cameron. I hope you won't mind I CC: this to -multimedia for a wider audience. :) I fleshed out my "rec" program. I wrote this thing about a year ago and now it seems to be in order. I mainly use it as a replacement for sox, since sox seems unsuitable to make hifi recordings. I think rec is great, and I intend to make a port out of it (or put it in the base system?) ;) Also, I would like to request your comments on the program itself. Note that the program records hifi by default (44100 Hz, 16 bits, 2 channels). Now I have a few problems with it: 1- pcm0: record overrun, dumping 178180 bytes Good old error message, do I like this one. :) I only have this problem when recording without time limit (-t 0/-f 0 or no -t/-f switch). Recording small samples (using -t 10) works fine. Strange. How can I debug this? 2- raw data filled As you described to me in another mail, in some circumstances where the hw support is not enough, the data stream is filled to pad bytes. Is this the case here: 00000000: 04fe 50fe 08fe 54fe 09fe 51fe 05fe 52fe ..P...T...Q...R. 00000010: 0efe 59fe 05fe 4dfe 07fe 4ffe 0afe 50fe ..Y...M...O...P. 00000020: 07fe 4ffe 04fe 58fe 0dfe 52fe fffd 55fe ..O...X...R...U. 00000030: 0afe 4dfe fefd 52fe 0bfe 5afe 02fe 4cfe ..M...R...Z...L. Sure does look like it. What do you think? 3- ioctls does not seem to work properly. At some point in the code, I call the ioctls: if (ioctl(fd, SOUND_PCM_READ_CHANNELS, ¶ms->r_channels)) err(1, "ioctl for read channels failed"); if (ioctl(fd, SOUND_PCM_WRITE_CHANNELS, ¶ms->w_channels)) err(1, "ioctl for write channels failed"); (for example). Now the value of params->*_channels is 2 in both cases. The output of the program, however, is: read write units DSP device : /dev/dspW /dev/dspW=20 Rate : 8000 44100 Hz Channels : 1 2 =20 Size : 16 16 bits ie, params->r_channels is *changed* from 2 to 1. Same goes for r_rate. I do not understand that behavior.=20 I know the test program is a bit crappy, especially in time calculations, but it does the job, and I understand it well. :) Please review the program, and tell me what is wrong here! I really need a hifi recording program for FreeBSD. The next step a multi-track mixing program, and I think I'm doing a good job at porting "mixmagic", a gnome mixing program. Anyways, the program is attached, only 2 are necessary to compile it, (rec.c, convert.h) but as the code tells, it also has primary support to record aiff sound files. For that it needs 4 more files (aiff.[ch], ieee_ext.[ch]). I include here only rec.c and convert.h. Read on... On Mon, 16 Apr 2001, Cameron Grant wrote: > > Uh-uh.. Ok. This is what I understood. This is very good. Does that mean > > I can do multiple open of /dev/dsp? I can't wait to try that out. :) >=20 > no, but multiple dsp devices are created. the device nodes are not creat= ed > by MAKEDEV under -stable, but if you read sys/dev/sound/pcm/sound.c and > create the appropriate nodes yourself multiple opens will work. this is > obviously not supported usage yet, however. Is this still applicable? I took a peak at sound.c and didn't really figure it out. > > OK. That is very good. And this was my concern in the first place. The > > problem (panic) is fixed under -stable but I'm not sure wether > > recording still works 100% correctly. There was still this overflow > > problem that was crippling recording. I think it is also fixed, but > > cannot confirm. I will check all this tonight, on my AWE32 and my SBC16 > > Vibra cards. :) >=20 > it did work when i tested it. >=20 > the overflow problem occurs when a recording application reads too slowly. > sox appears to read 4 bytes at a time, so the syscall overhead is extreme. > i don't understand how it is supposed to work as at 44khz 16bit stereo th= is > results in >44000 syscalls per second which requires a very fast machine. > other apps which use a larger buffer do work properly. this does not seem > to be related to newpcm. I tried various buffer sizes. The default is 1092 (BUFSIZ) and I tried up to 8192, without noticeable effect. I think the problem might be in the rec.c code itself, especially in the recording loop where time is (so badly) displayed and computed, but I am not sure. A. --5G06lTa6Jq83wMTw Content-Type: text/plain; charset=us-ascii Content-Description: Conversion routines Content-Disposition: attachment; filename="convert.h" /****************************-*-C-*-*********************************** * $Id: convert.h,v 1.5 2001/08/15 18:37:18 anarcat Exp $ ********************************************************************** * Conversion routines for sound parameters ********************************************************************** * Copyright (C) 2001 The Anarcat * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See also http://www.fsf.org *********************************************************************/ #ifndef CONVERT_H #define CONVERT_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef USE_AIFF /* Compute the "very large number" so that a maximum number of samples * can be transmitted through a pipe without the risk of causing * overflow when calculating the number of bytes. At 48 kHz, 16 bits * stereo, this gives ~3 hours of music. Sorry, the AIFF format does * not provide for an "infinite" number of samples. */ #define MAX_BYTES 0x7f000000L #endif #define MAX_FRAMES(size, channels) (MAX_BYTES / ((size)/8) * (channels)) #define MAX_TIME(size, channels, frames) \ (MAX_FRAMES(size, channels) / (frames)) #define BYTES_FROM_FRAMES(frames, size, channels) \ ((frames) * (channels) * ((size)/8)) #define SAMPLES_FROM_BYTES(bytes, size) \ ((bytes) / ((size) / 8)) #define FRAMES_FROM_BYTES(bytes, size, channels) \ (SAMPLES_FROM_BYTES(bytes, size) / (channels)) #define TIME_FROM_BYTES(bytes, size, channels, rate) \ (FRAMES_FROM_BYTES(bytes, size, channels) / (rate)) #define FRAMES_FROM_TIME(time, rate) \ ((time) * (rate)) #define SAMPLES_FROM_TIME(time, rate, channels) \ (FRAMES_FROM_TIME(time, rate) * (channels)) #define BYTES_FROM_TIME(time, rate, channels, size) \ (SAMPLES_FROM_TIME(time, rate, channels) * ((size)/8)) #endif --5G06lTa6Jq83wMTw Content-Type: text/plain; charset=us-ascii Content-Description: Record program Content-Disposition: attachment; filename="rec.c" Content-Transfer-Encoding: quoted-printable /****************************-*-C-*-*********************************** * $Id: rec.c,v 0.23 2001/08/15 21:09:43 anarcat Exp $ ********************************************************************** * DSP recording utility for FreeBSD ********************************************************************** * Copyright (C) 2000 The Anarcat <anarcat@tao.ca> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See also http://www.fsf.org *********************************************************************/ /* * This program can be used to record RAW (PCM) audio from the DSP device * * See the help (./rec -h) for more info * * Note that the resulting file can be easily converted to WAVE using sox: * * $ sox -c 2 -r 44100 -w -s file.raw file.wav */ #include <string.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <machine/soundcard.h> #include <err.h> #include <signal.h> #ifndef NDEBUG #include <assert.h> #include <sys/time.h> #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "convert.h" #ifdef USE_AIFF #include "ieee_ext.h" #include "aiff.h" #endif #define DFL_DEV "/dev/dspW" #define DFL_SIZE 16 #define DFL_CHAN 2 #define DFL_RATE 44100 int snddev; /* sound device to read from */ #ifdef USE_AIFF AIFFSound aiffSnd; /* sound to write */ #endif int sndFile; /* file to write it to */ #ifndef NDEBUG struct timeval start; /* start of recording */ struct timeval end; /* end of recording */ #endif typedef struct _dsp_parameters { char* device; /* device name */ unsigned int r_rate; /* read sample rate */ unsigned int w_rate; /* write sample rate */ unsigned int r_channels; /* channels to use (1: mono, 2: stereo) */ unsigned int w_channels; /* write channels */ unsigned int r_size; /* read sample size (8 bits or 16 bits) */ unsigned int w_size; /* write sample size */ =20 } dsp_parameters; /* handles an interrupt in raw readings */ void quit(int signum) { #ifndef NDEBUG long int secs, msecs; #endif fprintf(stderr, "\n"); #ifndef NDEBUG assert(!gettimeofday(&end, NULL)); =20 secs =3D end.tv_sec - start.tv_sec; msecs =3D end.tv_usec - start.tv_usec; if (msecs < 0) { /* keep ms > 0 */ secs--; msecs +=3D 1000000; } assert (msecs >=3D 0); fprintf(stderr, "recording duration: %li.%6.6li secs\n",=20 secs,msecs); #endif close(snddev); close(sndFile); if (signum > 0) { fprintf(stderr, "Finished recording with signal %d\n", signum); exit(0); } else { fprintf(stderr, "Finished recording\n"); exit(0); } =20 } #ifdef USE_AIFF /* handles interrupts from aiff routines */ void stopRec(int signum) { stopRecording(sndFile, &aiffSnd); =20 fprintf(stderr, "\nWrote %d frames with %d bytes of raw sound data\n", aiffSnd.comm.numSampleFrames, aiffSnd.ssnd.ckDataSize); writeAIFFSound(sndFile, &aiffSnd); quit(signum); =20 } #endif /** * Initialises the DSP device *=20 * Returns the fd of the open call **/ int initDSP(dsp_parameters *params ) { int fd =3D open(params->device, O_RDONLY); =20 if (fd < 0) err(1,"can't open DSP device %s", params->device); =20 if (ioctl(fd, SOUND_PCM_READ_BITS, ¶ms->r_size)) err(1, "ioctl for read sample size failed"); if (ioctl(fd, SOUND_PCM_WRITE_BITS, ¶ms->w_size)) err(1, "ioctl for write sample size failed"); if (ioctl(fd, SOUND_PCM_READ_CHANNELS, ¶ms->r_channels)) err(1, "ioctl for read channels failed"); if (ioctl(fd, SOUND_PCM_WRITE_CHANNELS, ¶ms->w_channels)) err(1, "ioctl for write channels failed"); if (ioctl(fd, SOUND_PCM_READ_RATE, ¶ms->r_rate)) err(1, "ioctl for read sample rate failed"); if (ioctl(fd, SOUND_PCM_WRITE_RATE, ¶ms->w_rate)) err(1, "ioctl for write sample rate failed"); return fd; } /* end initDSP() */ /* a basic usage */ void usage(char* argvo) { fprintf(stderr, "\ usage: %s [-r rate] [-c channels] [-s size | -b | -w] [-h] [-d dev] [-t time | -f frames] file.[raw|aiff]\n", argvo); } /* end usage() */ /* more elaborate help */ void help(char* argvo) { usage(argvo); fprintf(stderr, " where: (defaults) -g size: buffer size to use (%d bytes) -d device: DSP device to use (%s) -r rate: sets sample rate to rate (%d Hz) -c channels: number of channels to use (%d) -s size: sample size in bits (%d bits) -f samples: number of samples to record (0, \"unlimited\") -t time: maximum recording time (0, \"unlimited\") -b: use 8 bits samples, same as '-s 8' -w: use 16 bits samples, same as '-s 16'\n" #ifdef USE_AIFF "-a: force AIFF output -p: force RAW PCM output" #endif "-h: gives this help Records a sound file" #ifdef USE_AIFF " in the AIFF format if the filename ends with .aiff or .aif or if the flag -i is specified, or " #endif "in a RAW PCM format (signed " #ifdef USE_AIFF "\n" #endif "linear 2's complement, " #ifndef USE_AIFF "\n" #endif "little endian)" #ifdef USE_AIFF " otherwise" #endif ". If time or a number of samples is specified, the recording will not be longer than that (in seconds or frames), if not, rec will stop only for a write failure or interruption (SIGINT/SIGHUP). When conflicting flags (b, w, s, and f, time) are specified, the latter overrides the former.\n\n",=20 BUFSIZ, DFL_DEV, DFL_RATE, DFL_CHAN, DFL_SIZE); } /* end help */ /* main */ int main(int argc, char *argv[]) { int toread =3D -1; /* what's left to read */ #ifdef USE_AIFF short aiffFormat =3D -1; /* boolean */ #endif int bufsize =3D BUFSIZ; =20 dsp_parameters params; /* how to write it */ =20 int tmp; =20 /* setting defaults */ params.device =3D DFL_DEV; params.r_rate =3D DFL_RATE; params.w_rate =3D DFL_RATE; params.r_channels =3D DFL_CHAN; params.w_channels =3D DFL_CHAN; params.r_size =3D DFL_SIZE; params.w_size =3D DFL_SIZE; /********************************************************************* * Command line parsing ********************************************************************/ { /* parsing of the options */ char ch; /* used by getopt */ char *endptr =3D 0; =20 while ((ch =3D getopt(argc, argv, "t:f:d:r:c:s:g:bw" #ifdef USE_AIFF "ap" #endif "h?")) !=3D -1) { switch (ch) { case 'g': /* buffer size */ tmp =3D strtol(optarg, &endptr, 0); if (*endptr) { /* oups, did not stop at the end */ fprintf (stderr, "invalid bufsize spec: %s\n", optarg); exit(1); } else if (!*optarg) { fprintf (stderr, "empty bufsize spec\n"); exit(1); } =20 if (tmp >=3D 0)=20 bufsize =3D tmp; else { fprintf (stderr,=20 "negative sample count specification: %d\n",=20 tmp); exit(1); } break; case 'd': /* sound device */ params.device =3D optarg; break; case 'r': /* sample rate */ tmp =3D atoi(optarg); if (tmp > 0) params.r_rate =3D tmp; else fprintf(stderr, "invalid sample rate spec: %s\n", optarg); break; case 'c': /* channel count */ tmp =3D atoi(optarg); if (tmp > 0) params.r_channels =3D tmp; else fprintf(stderr, "invalid channel count spec: %s\n", optarg); break; case 's': /* sample size (8 or 16) */ tmp =3D atoi(optarg); if (tmp > 0) { if (tmp !=3D 8 && tmp !=3D 16) errx(1, "unsupported sampling size: %d, should be 8 or 16", tmp= ); params.r_size =3D tmp; } else fprintf(stderr, "invalid sample size spec: %s\n", optarg); break; case 'f': /* frame count */ tmp =3D strtol(optarg, &endptr, 0); if (*endptr) { /* oups, did not stop at the end */ fprintf (stderr, "invalid frame spec: %s\n", optarg); exit(1); } else if (!*optarg) { fprintf (stderr, "empty frame spec\n"); exit(1); } =20 if (tmp >=3D 0)=20 toread =3D BYTES_FROM_FRAMES (tmp, params.r_size, params.r_channels); else { fprintf (stderr,=20 "negative sample count specification: %d\n",=20 tmp); exit(1); } assert (toread >=3D 0); break; case 't': /* time count */ toread =3D strtol(optarg, &endptr, 0); if (*endptr) { /* oups, did not stop at the end */ fprintf (stderr, "invalid time spec: %s\n", optarg); exit(1); } else if (!*optarg) { fprintf (stderr, "empty time spec\n"); exit(1); } if (toread >=3D 0) toread =3D BYTES_FROM_TIME(toread, params.r_rate,=20 params.r_channels, params.r_size); else { fprintf(stderr, "negative time specification: %d\n", toread); exit(1); } assert (toread >=3D 0); break; #ifdef USE_AIFF case 'a': /* force aiff output */ aiffFormat =3D 1; break; case 'p': /* force pcm raw output */ aiffFormat =3D 0; break; #endif case 'b': /* byte samples (8 bits) */ params.r_size =3D 8; break; case 'w': /* long samples (16 bits) */ params.r_size =3D 16; break; case 'h': /* long help */ help(argv[0]); exit(1); break; case '?': /* usage */ default: /* error */ usage(argv[0]); exit(1); break; } } } /* end arg parsing */ =20 argc -=3D optind; argv +=3D optind; if (argc < 1) /* examine filename */ errx(1, "No filename given, aborting"); else { #ifdef USE_AIFF int len =3D strlen(argv[0]); if (len > 3) { if (!strncmp (&argv[0][len-4], ".aif", 4)) aiffFormat =3D 1; else if (len > 4 && !strncmp (&argv[0][len-5], ".aiff", 5)) aiffFormat =3D 1; else { if (strncmp (&argv[0][len-4], ".raw", 4)) fprintf(stderr, "Unknown extension, using raw\n"); aiffFormat =3D 0; } } else { fprintf(stderr, "No extension given, using raw\n"); aiffFormat =3D 0; } #endif if (!strcmp (argv[0], "-")) { /* file is stdout */ sndFile =3D STDOUT_FILENO; } else { sndFile =3D open(argv[0], O_WRONLY | O_CREAT | O_TRUNC, 0644); if (sndFile < 0) err(1, "can't open output file %s", argv[0]); } } =20 /* here we should have at least the filename */ assert(argc >=3D 1); =20 /********************************************************************* * Examine Time ********************************************************************/ if (toread <=3D 0) { /* no time given */ #ifdef USE_AIFF /* here we should check for freespace in case of PCM records */ if (aiffFormat) { toread =3D MAX_BYTES; fprintf(stderr, "Recording until INT (control-c) or %li seconds.\n", MAX_TIME(params.r_size, params.r_channels, params.r_rate)); } else { /* pcm recording */ #endif toread =3D 0; /* this should be get_freespace(path) */ fprintf(stderr, "Recording until INT (control-c).\n"); #ifdef USE_AIFF } #endif } else fprintf(stderr, "Recording %d seconds (%d frames), send INT (control-c) to stop= \n", TIME_FROM_BYTES(toread, params.r_size, params.r_channels, params.r_rate), FRAMES_FROM_BYTES(toread, params.r_size, params.r_channels)); /* (toread / (params.r_size/8)) / params.r_channels) */; =20 snddev =3D initDSP(¶ms); /* initialize DSP */ #ifdef USE_AIFF aiffSnd.ssnd.fd =3D snddev; #endif /********************************************************************* * Print recording stats ********************************************************************/ fprintf(stderr, "\ read write units DSP device : %-10s %1$-10s Rate : %-10d %-10d Hz Channels : %-10d %-10d Size : %-10d %-10d bits Output format: ", params.device, params.r_rate, params.w_rate, params.r_channels, params.w_channels, params.r_size, params.w_size); #ifdef USE_AIFF if (aiffFormat) fprintf(stderr, "AIFF\n"); else #endif fprintf(stderr, "PCM signed linear 2's complement\n"); #ifdef USE_AIFF =20 if (aiffFormat) { =20 signal(SIGINT, stopRec); /* handle signals smoothly */ signal(SIGHUP, stopRec); aiffSnd.comm.numChannels =3D params.w_channels; aiffSnd.comm.sampleSize =3D params.w_size; doubleToIEEEExtended((double)params.w_rate, aiffSnd.comm.sampleRate); aiffSnd.ssnd.ckDataSize =3D toread + 8; updateSizeFields(&aiffSnd); #ifndef NDEBUG assert(!gettimeofday(&start, NULL)); #endif if (writeAIFFSound(sndFile, &aiffSnd) < 0) warn("error in writeAIFFSound"); stopRec(0); =20 } else /* RAW recording */ #endif { int readcount, time, eta; const int origToread =3D toread; int never_stop =3D 0; char *buffer =3D (char*) malloc(bufsize); if (!buffer) { perror ("can't allocate memory for read buffer"); exit(1); } =20 if (toread <=3D 0) { never_stop =3D 1; toread =3D bufsize; } signal( SIGINT, quit ); signal( SIGHUP, quit ); =20 #ifndef NDEBUG assert(!gettimeofday(&start, NULL)); #endif =20 while (never_stop || toread > 0) { readcount =3D bufsize; if (readcount > toread) readcount =3D toread; readcount =3D read(snddev, buffer, readcount); if (readcount < 0) { perror("dspread"); exit(1); } if (write(sndFile, buffer, readcount) < 0) { perror("failed to write to file"); exit(1); } toread -=3D readcount; =20 eta =3D TIME_FROM_BYTES(toread, params.w_size, params.w_channels, params.w_rate); time =3D TIME_FROM_BYTES((origToread - toread), params.w_size, params.w_channels, params.w_rate); fprintf(stderr, "\r%10d of %10d bytes read, %d:%02d:%02d / ETA %d:%= 02d:%02d", origToread - toread, origToread, time/360, (time%360)/60, time % 60,=20 eta/360, (eta%360) / 60, eta % 60); } quit(0); } return 0; /* not reached */ } /* end main() */ --5G06lTa6Jq83wMTw-- --ZJcv+A0YCCLh2VIg Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.6 (FreeBSD) Comment: For info see http://www.gnupg.org iEYEARECAAYFAjt65mEACgkQttcWHAnWiGdKnQCdF33Ry8d8X9rwklMkxTKlN4XW +3IAn2hdk7BBX/bGylIGt3FPuDgZkoqU =iepB -----END PGP SIGNATURE----- --ZJcv+A0YCCLh2VIg-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-multimedia" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010815171514.B471>