Any way to get a better format than 16bit when opening an output in ALSA, with SDL?

Hi there. Using C++ on SDL2’s audio interface (working on a performance grade synthesizer, porting a vst) to write samples direct to the audio out port.

  • On my Mac I am able to open SDL2/AppleCoreAudio with Float32 samples for the audio output,
  • On the rPI w/pisound, I can only open SDL2/ALSA with S16 (signed 16 bit integer samples).

Obviously this will be a little lower in quality than Float32 (though it’s possible the mac is auto converting to S16 for me, I dont know).

Questions:

  • I know the pisound has a high quality DAC and components, but what’s the software to hardware DAC conversion look like, I see the pisound hardware supports 24bit here: https://blokas.io/pisound/docs/Specs/.
  • Given that, what is the best I should hope for from ALSA? Is 32 bit float (or int) possible?
  • Should I be searching for a way to configure ALSA to something better than S16, or is this the best it gets? (i see alsa plug conversion stuff, but looking to avoid if all it does is convert to s16 anyways).
  • and, if I should be configuring ALSA / pisound for better than S16, any ideas how?
  • anything better than ALSA that SDL2 would support? (i hear things about jack but have no experience, appears SDL2 supports jack now, so I could try that)

So… I think ALSA supports 32bit float - see the man page and it’s list of formats.
https://linux.die.net/man/1/aplay

Is it possible SDL2 does not support 32bit float files on linux?

Or perhaps the files are saved on a way that is not working properly:

I think the key in your linked article on aplay audio formats is:

Some of these may not be available on selected hardware

aplay -f S24_LE playing.wav
Warning: format is changed to S16_LE
Playing WAVE 'playing.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Warning: rate is not accurate (requested = 44100Hz, got = 48000Hz)
         please, try the plug plugin (-Dplug:default)

aplay wont use just any format, it “snaps” to what the hardware supports, apparently: “Warning: format is changed to S16_LE”

aplay -f FLOAT_LE playing.wav
Warning: format is changed to S16_LE
Playing WAVE 'playing.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
Warning: rate is not accurate (requested = 44100Hz, got = 48000Hz)
         please, try the plug plugin (-Dplug:default)

Same thing with the plughw alsa plugin, again we get “Warning: format is changed to S16_LE”

aplay -f S24_LE -Dplughw:CAR D=pisound,DEV=0 playing.wav
Warning: format is changed to S16_LE
Playing WAVE 'playing.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo

Looking at aplay’s and arecord’s formats supported:

aplay --dump-hw-params playing.wav
Playing WAVE 'playing.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
HW Params of device "default":
--------------------
ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  S16_LE S24_LE S32_LE
SAMPLE_BITS: [16 32]
FRAME_BITS: [32 64]
CHANNELS: 2
RATE: [48000 192000]

and arecord:

arecord --dump-hw-params
Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
HW Params of device "default":
--------------------
FORMAT:  S16_LE S24_LE S32_LE
SAMPLE_BITS: [16 32]
FRAME_BITS: [32 64]
CHANNELS: 2
RATE: [48000 192000]
...
arecord: set_params:1299: Sample format non available
Available formats:
- S16_LE
- S24_LE
- S32_LE

But S24_LE and S32_LE do not seem available if aplay is always going to “snap” back to S16_LE

Warning: format is changed to S16_LE

Is it possible SDL2 does not support 32bit float files on linux?

Or perhaps the files are saved on a way that is not working properly:

[How to get SDL2 to play 32bit WAV files]

By the way, my SDL app is not playing WAV files :slight_smile: Imagine using sinf( x ) and piping that direct to the audio buffer… (of course more complicated with envelopes and filters). It’s a super common technique to use 32bit floats for calculating this sound synthesis in c++. Again, I know how to convert to 16bit ints, however, I’m worried about quality, that I’m not utilizing the pisound’s 24bit DAC fully if I go down to 16bit, so I’d like to get ALSA set up right.

Surely not everyone is running ALSA constrained at 16bit, with a nice 24bit pisound hardware??

I even SEE from aplay that the hardware supports 16, 24, and 32 integer! so… maybe my alsa isn’t setup right? maybe it’s my .asoundrc?

Testing, since I see S32_LE in the list of supported formats for aplay --dump-hw-params:
Opening audio output port with SDL2 as AUDIO_S32LSB (32bit signed integer) also falls back to AUDIO_S16LSB (same as was happening above with AUDIO_F32)

illustrating the fallback that is happening:

 int device_id = 2; // pisound's device #
 SDL_zero(want);
 want.freq = 44100;
 want.channels = 2;
 want.callback = SDL_AudioCallback;
 //want.format = AUDIO_F32;
 want.format = AUDIO_S32LSB;
 want.samples = 1024;
 printf( "We _want_ audio with\n" );
 printFormat( want.format );
 
 dev = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, 0), 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
 if (dev == 0) {
   printf("Failed to open audio: %s\n", SDL_GetError());
   exit(0);
 } else {
   if (have.format != want.format)  // we let this one thing change.
   {
     printf("We didn't get the requested audio format.\n");
     printFormat( have.format );
   }
   SDL_PauseAudioDevice(dev, 0);  // start audio playing.
}

and I get the output:

We want audio with
integer data
32 bits per sample

We didn’t get the requested audio format.
integer data
16 bits per sample

Basically SDL tries to open S32 (my “want”) but can’t do it, picks one that it can open, S16 and tells me using the “have” above

I’m not sure I really understand all this, but I am curious.

Could you post/link a sample 32bit float audio file? (I don’t have a pisound, but am curious to test on my own audio hardware)

Have you tried testing with Jack yet?

Good point.

I will generate a few test files other than 16bit and see if aplay and my hardware can handle that!

I’ll post as well once I create them… if you are interested to try

I do not have Jack set up. That I know of. Need to learn something about that. However I heard jack sits on ALSA, so I think there’s no harm going direct to ALSA

I guess I just need an example of connecting using better format than 16 bit :wink:

I think one problem is the frame rate (want.freq = 44100), you must use 48000 or 192000 if you want to write to the pisound card.

root@modep:~# alsabat -P hw:CARD=pisound,DEV=0 -c 2 -f S32_LE -r 48000 -F 440
Return value is 0

root@modep:~# alsabat -P hw:CARD=pisound,DEV=0 -c 2 -f S32_LE -r 192000 -F 440
Return value is 0

root@modep:~# alsabat -P hw:CARD=pisound,DEV=0 -c 2 -f S32_LE -r 44100 -F 440
Invalid parameters: sample rate: requested 44100Hz, got 48000Hz
Exit playback thread fail: -22

Is “SDL_GetAudioDeviceName(0, 0)” the pisound card?

Edit: sample rate 96000 works also

Edit2: with plughw:CARD=pisound,DEV=0 it works, but ALSA probably converts it on the fly.

root@modep:~# alsabat -P plughw:CARD=pisound,DEV=0 -c 2 -f S32_LE -r 44100 -F 440

2 Likes

Well, thank you for pointing out my bug:

printf( “Opening device %d %s…\n”, device_id, SDL_GetAudioDeviceName(device_id, 0));
dev = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, 0), 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);

Indeed, my pisound is device #2… And obviously I am not carrying the device_id into the SDL_OpenAudioDevice which opens device 0!

This may solve the issue… I’ll try tonight and report back.

And… that was it! I was writing audio to the wrong device.

I was able to open as S32 (signed 32 bit integer) format on the pisound port, using SDL.

Success!

Thank you for the sanity check, and the eagle eye on my audio port open in SDL! Gratitude!

1 Like

Pisound’s natively supported sampling rates are 48000, 96000 and 192000, and its ADC and DAC are capable of 24 bit precision, but accepts 32 bit PCM data as well, as it’s a more convenient amount of data to work with for sample processing.

1 Like