| Summary: | PCM sound driver silently accepts improper parameters of SNDCTL_DSP_SETFMT | ||
|---|---|---|---|
| Product: | Base System | Reporter: | Andre Albsmeier <Andre.Albsmeier> |
| Component: | kern | Assignee: | cg |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | 4.1-STABLE | ||
| Hardware: | Any | ||
| OS: | Any | ||
Responsible Changed From-To: freebsd-bugs->cg Over to maintainer. SNDCTL_DSP_GETFMTS returns the hardware formats supported.
SNDCTL_DSP_SETFMT is allowed to set other formats using software
translation, and does so.
-cg
[CC'ed to Randall Hopper <aa8vb@nc.rr.com> as well since we both are currently lost with this] On Wed, 06-Sep-2000 at 15:03:50 +0100, Cameron Grant wrote: > SNDCTL_DSP_GETFMTS returns the hardware formats supported. > SNDCTL_DSP_SETFMT is allowed to set other formats using software > translation, and does so. Hmm, let's see if I understood this correctly: In case SNDCTL_DSP_GETFMTS returns 8 (don't worry about stereo) which is AFMT_U8 and I set 0x10 (AFMT_S16_LE) and get returned 0x10 I can assume that: 1. Recording will be done properly (but possibly with a different format) 2. The output data will conform to the format set with SNDCTL_DSP_SETFMT which is AFMT_S16_LE in my example. Is this correct? Thanks for looking into this, -Andre > On Wed, 06-Sep-2000 at 15:03:50 +0100, Cameron Grant wrote: > > SNDCTL_DSP_GETFMTS returns the hardware formats supported. > > SNDCTL_DSP_SETFMT is allowed to set other formats using software > > translation, and does so. > > Hmm, let's see if I understood this correctly: > > In case SNDCTL_DSP_GETFMTS returns 8 (don't worry about stereo) which > is AFMT_U8 and I set 0x10 (AFMT_S16_LE) and get returned 0x10 I can > assume that: > > 1. Recording will be done properly (but possibly with a different format) > 2. The output data will conform to the format set with SNDCTL_DSP_SETFMT > which is AFMT_S16_LE in my example. > > Is this correct? yes. in theory at least. if you get garbage out, there's a bug in the code. -cg On Wed, 06-Sep-2000 at 15:28:18 +0100, Cameron Grant wrote:
> > On Wed, 06-Sep-2000 at 15:03:50 +0100, Cameron Grant wrote:
> > > SNDCTL_DSP_GETFMTS returns the hardware formats supported.
> > > SNDCTL_DSP_SETFMT is allowed to set other formats using software
> > > translation, and does so.
> >
> > Hmm, let's see if I understood this correctly:
> >
> > In case SNDCTL_DSP_GETFMTS returns 8 (don't worry about stereo) which
> > is AFMT_U8 and I set 0x10 (AFMT_S16_LE) and get returned 0x10 I can
> > assume that:
> >
> > 1. Recording will be done properly (but possibly with a different format)
> > 2. The output data will conform to the format set with SNDCTL_DSP_SETFMT
> > which is AFMT_S16_LE in my example.
> >
> > Is this correct?
>
> yes. in theory at least. if you get garbage out, there's a bug in the
> code.
Thanks. I think the latter is correct :-). I will investigate this a bit
more...
BTW, is my AWE 64 really not possible to do native 16 Bit recording?
IIRC, with voxware I could do 16 Bit recording but I might be wrong...
Thanks,
-Andre
On Wed, 06-Sep-2000 at 16:37:28 +0200, Andre Albsmeier wrote:
> On Wed, 06-Sep-2000 at 15:28:18 +0100, Cameron Grant wrote:
> > > On Wed, 06-Sep-2000 at 15:03:50 +0100, Cameron Grant wrote:
> > > > SNDCTL_DSP_GETFMTS returns the hardware formats supported.
> > > > SNDCTL_DSP_SETFMT is allowed to set other formats using software
> > > > translation, and does so.
> > >
> > > Hmm, let's see if I understood this correctly:
> > >
> > > In case SNDCTL_DSP_GETFMTS returns 8 (don't worry about stereo) which
> > > is AFMT_U8 and I set 0x10 (AFMT_S16_LE) and get returned 0x10 I can
> > > assume that:
> > >
> > > 1. Recording will be done properly (but possibly with a different format)
> > > 2. The output data will conform to the format set with SNDCTL_DSP_SETFMT
> > > which is AFMT_S16_LE in my example.
> > >
> > > Is this correct?
> >
> > yes. in theory at least. if you get garbage out, there's a bug in the
> > code.
>
> Thanks. I think the latter is correct :-). I will investigate this a bit
> more...
>
> BTW, is my AWE 64 really not possible to do native 16 Bit recording?
> IIRC, with voxware I could do 16 Bit recording but I might be wrong...
I assume there is really a problem when recording a 16 Bit format
with a soundcard that is only capable of doing AFMT_U8...
Randall, can I include your dsp-record.c in a mail for Cameron?
This would make explaining the problem much easier...
Thanks,
-Andre
Andre Albsmeier: |On Wed, 06-Sep-2000 at 16:37:28 +0200, Andre Albsmeier wrote: |> BTW, is my AWE 64 really not possible to do native 16 Bit recording? |> IIRC, with voxware I could do 16 Bit recording but I might be wrong... | |I assume there is really a problem when recording a 16 Bit format |with a soundcard that is only capable of doing AFMT_U8... | |Randall, can I include your dsp-record.c in a mail for Cameron? |This would make explaining the problem much easier... Sure. And yes, with Voxware 16-bit stereo recording is no problem for cards with a SB16-compatible DSP (SB16, SB32, AWE32, AWE64, etc.). Mine is a SB32. -- Randall Hopper aa8vb@nc.rr.com On Wed, 06-Sep-2000 at 15:28:18 +0100, Cameron Grant wrote: > > On Wed, 06-Sep-2000 at 15:03:50 +0100, Cameron Grant wrote: > > > SNDCTL_DSP_GETFMTS returns the hardware formats supported. > > > SNDCTL_DSP_SETFMT is allowed to set other formats using software > > > translation, and does so. > > > > Hmm, let's see if I understood this correctly: > > > > In case SNDCTL_DSP_GETFMTS returns 8 (don't worry about stereo) which > > is AFMT_U8 and I set 0x10 (AFMT_S16_LE) and get returned 0x10 I can > > assume that: > > > > 1. Recording will be done properly (but possibly with a different format) > > 2. The output data will conform to the format set with SNDCTL_DSP_SETFMT > > which is AFMT_S16_LE in my example. > > > > Is this correct? > > yes. in theory at least. if you get garbage out, there's a bug in the > code. OK, here is more info: Randall has written a small program dsp-record.c (I have just modified it a bit for -Wall): ------------------------- snip ----------------------- /* Adapted from short pgm posted by dnelson@emsphone.com by */ /* Randall Hopper (rhh@ct.picker.com) */ #include <stdio.h> #include <ctype.h> #include <string.h> #include <sys/soundcard.h> #include <sys/time.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #define ARRAYSIZE(a) ( sizeof(a) / sizeof(a[0]) ) #define DO_IOCTL_SERR(str,arg) \ do { fprintf(stderr, "ioctl(%s, %ld) failed: %s\n", \ str, (long)arg, strerror(errno) ); \ exit(1); \ } while (0) #define DO_IOCTL_SVALERR(str,v1,v2) \ do { fprintf(stderr, "ioctl(%s) failed: Tried %d, Got %d\n", \ str, v1, v2 ); \ exit(1); \ } while (0) void StrLwr( char a[] ) { char *p = a; while ( *p != '\0' ) *(p++) = tolower( *p ); } /* SampFmt_StrToEnum: Convert sample format string to enum; -1 on failure */ int SampFmt_StrToEnum( char str[] ) { static struct { char *str; int val; } Sfmtcnvt[] = { { "mulaw", AFMT_MU_LAW }, { "ulaw" , AFMT_MU_LAW }, { "alaw" , AFMT_A_LAW }, { "adpcm", AFMT_IMA_ADPCM }, { "8" , AFMT_U8 }, { "u8" , AFMT_U8 }, { "16" , AFMT_S16_LE }, { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, { "s8" , AFMT_S8 }, { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, { "mpeg" , AFMT_MPEG }, }; int i; char tmpstr[20]; tmpstr[0] = '\0'; strncat( tmpstr, str, sizeof(tmpstr)-1 ); StrLwr( tmpstr ); for ( i = 0; i < ARRAYSIZE( Sfmtcnvt ); i++ ) if ( strcmp( tmpstr, Sfmtcnvt[i].str ) == 0 ) break; if ( i < ARRAYSIZE( Sfmtcnvt ) ) return Sfmtcnvt[i].val; else return -1; } int main(int argc, char *argv[]) { int fd; int rate = 8012; int bits = 8; int rate_new, bits_new, chan_new; char bits_str[80] = "u8"; int chan = 1; int duration = -1, elapsed; int c; int len; char buf[1024]; struct timeval tv1,tv2; int frag, frag_new; if ( (fd = open("/dev/dsp", O_RDONLY)) < 0 ) { fprintf( stderr, "Can't open /dev/dsp for read\n" ); exit(1); } while ((c = getopt(argc, argv, "hr:b:c:d:")) != EOF) { switch (c) { case 'r': rate = atoi(optarg); break; case 'b': bits_str[0] = '\0'; strncat( bits_str, optarg, sizeof(bits_str)-1 ); bits = SampFmt_StrToEnum( bits_str ); if ( bits < 0 ) { fprintf( stderr, "Bad sample format\n" ); exit(1); } break; case 'c': chan = atoi(optarg); break; case 'd': duration = atoi(optarg); break; case '?': case 'h': fprintf( stderr, "dsp-record -r rate -b bits -c channels " "-d duration(sec)\n"); exit(1); } } fprintf( stderr, "Trying %d Hz %d Channel %s Format\n", rate, chan, bits_str ); bits_new = bits; if ( ioctl(fd, SNDCTL_DSP_SETFMT, &bits_new) < 0 ) DO_IOCTL_SERR( "SNDCTL_DSP_SETFMT", bits ); if ( bits != bits_new ) DO_IOCTL_SVALERR( "SNDCTL_DSP_SETFMT", bits, bits_new ); rate_new = rate; if ( ioctl(fd, SOUND_PCM_WRITE_RATE, &rate_new) < 0 ) DO_IOCTL_SERR( "SOUND_PCM_WRITE_RATE", rate ); if ( rate != rate_new ) DO_IOCTL_SVALERR( "SOUND_PCM_WRITE_RATE", rate, rate_new ); chan_new = chan; if ( ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &chan_new) < 0 ) DO_IOCTL_SERR( "SOUND_PCM_WRITE_CHANNELS", chan ); ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &chan_new); if ( chan != chan_new ) DO_IOCTL_SVALERR( "SOUND_PCM_WRITE_CHANNELS", chan, chan_new ); fprintf( stderr, "Recording %d Hz %d Channel %s Format\n", rate, chan, bits_str ); frag_new = frag = (127 << 16) | 10; if ( ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag_new) < 0 ) DO_IOCTL_SERR( "SNDCTL_DSP_SETFRAGMENT", frag ); if ( (frag_new & 0xffff) != 10 ) { fprintf( stderr, "SNDCTL_DSP_SETFRAGMENT: Can't set 1k buffers\n" ); exit(1); } gettimeofday( &tv1, NULL ); tv1.tv_sec--, tv1.tv_usec += 1000000; while ((len = read(fd, buf, sizeof(buf))) > 0) { write(fileno(stdout), buf, len); if ( duration > 0 ) { gettimeofday( &tv2, NULL ); elapsed = (tv2.tv_sec - tv1.tv_sec ) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000; /*fprintf( stderr, "sec = %d\r", elapsed/1000 );*/ if ( elapsed/1000 >= duration ) break; } } close(fd); fprintf( stderr, "\n" ); if ( len <= 0 ) { fprintf( stderr, "read() failed; errno = %s\n", strerror(errno) ); exit(1); } exit(0); } ------------------------- snip ----------------------- Now what we do is following: We first record 3 seconds of sound in AFMT_U8. Then we transform it into a WAV file to make it hearable. Finally, we examine the result. andre@bali:~/try>./dsp-record -r 44100 -b 8 -c 2 -d 3 > 8.raw Trying 44100 Hz 2 Channel 8 Format Recording 44100 Hz 2 Channel 8 Format andre@bali:~/try>sox -t raw -r 44100 -u -b 8.raw -t wav -r 44100 -s -w 8.wav andre@bali:~/try>pcmplay 8.wav This works great for AFMT_U8 and AFMT_S8. Now we do the same using a 16 Bit format, AFMT_U16_LE: andre@bali:~/try>./dsp-record -r 44100 -b u16le -c 2 -d 3 > 16.raw Trying 44100 Hz 2 Channel u16le Format Recording 44100 Hz 2 Channel u16le Format andre@bali:~/try>sox -t raw -r 44100 -u -w 16.raw -t wav -r 44100 -s -w 16.wav andre@bali:~/try>pcmplay 16.wav This could ruin my speakers :-). If this is the correct way of testing it, there is something wrong with the PCM driver or its format conversion. Another thing Randall confirmed is that he also could do 16 Bit recordings with his SB32 and voxware. Is this feature known to be missing in the PCM driver because it is not implemented or should it work here as well? Thanks, -Andre Please close this one, it is fixed... -Andre State Changed From-To: open->closed Closed at submitters request. |
When setting a sound format with SNDCTL_DSP_SETFMT, the system call does not tell us if an improper format was set. How-To-Repeat: The following program demonstrates the error: #include <stdio.h> #include <ctype.h> #include <string.h> #include <machine/soundcard.h> #include <sys/time.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main( ) { int bits; int fd; if( (fd = open("/dev/dsp", O_RDONLY)) < 0 ) { fprintf( stderr, "Can't open /dev/dsp for read\n" ); exit( 1 ); } if( ioctl(fd, SNDCTL_DSP_GETFMTS, &bits) < 0 ) { fprintf( stderr, "SNDCTL_DSP_GETFMTS error\n" ); exit( 1 ); } printf( "GETFMTS: 0x%X\n", bits ); bits = 0x10; if( ioctl(fd, SNDCTL_DSP_SETFMT, &bits) < 0 ) { fprintf( stderr, "SNDCTL_DSP_SETFMT error\n" ); exit( 1 ); } printf( "After setting 0x10 with SETFMT: 0x%X\n", bits ); return( 0 ); } andre@bali:~>./sndcheck GETFMTS: 0x10000008 After setting 0x10 with SETFMT: 0x10 So the AWE64 only supports AFMT_U8 in STEREO. Now we set AFMT_S16_LE but there is no error returned neither was the bits variable modified as it was the case in voxware.