Re: [alsa-devel] [Alsa-user] How to get set/fixed sample rate of ALSA device?
On 10/07/2007 07:04 PM, Peteris Krisjanis wrote:
"Current sample rate" only has meaning with the device open , and if I understand you right, a "cat /proc/asound/card0/pcm0{p,c}/sub0/hw_params will then get you what you want as to the "get" part (substitute for 0 in the above as required).
I actually meant what kind of sample rate is set in sound card. For example, my onboard sound card has fixed Hz. I would like to know such information _before_ I touch sound card :)
Okay, you want the information that the driver author supplied in the struct snd_pcm_hardware for the stream. I've in fact also wanted that information on a number of occasions and just looked at the driver source then.
So you suggest to open device via any means and then read that file? I did this and it works. I guess I can even try to implement it in app and check for sample rate everytime before I play.
But is this only solution? It's rather hack, and I would like to have more reliable method.
You might consider this less of of a hack -- it uses the ALSA API to query the information from the PCM handle directly. Please see the library docoumentation for other information you can extract from a hw_params struct.
(I don't suppose you can ever get anything but dir == 0 for "hw" devices).
Introducing a /proc/asound/card0/pcm0{p,c}/hw_limits or similar might not be a bad idea though?
Rene
/* gcc -W -Wall -o snd_rate snd_rate.c -l asound */
#include <stdio.h> #include <alsa/asoundlib.h>
int main(void) { snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int val; int dir; int err;
snd_pcm_hw_params_alloca(¶ms);
err = snd_pcm_open(&handle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(err)); return -1; }
err = snd_pcm_hw_params_any(handle, params); if (err < 0) { fprintf(stderr, "snd_pcm_hw_params_any: %s\n", snd_strerror(err)); return -1; } err = snd_pcm_hw_params_get_rate_min(params, &val, &dir); if (err < 0) { fprintf(stderr, "snd_pcm_hw_params_get_rate_min: %s\n", snd_strerror(err)); return -1; } printf("min: (%c) %u\n", "<=>"[dir + 1], val);
err = snd_pcm_hw_params_get_rate_max(params, &val, &dir); if (err < 0) { fprintf(stderr, "snd_pcm_hw_params_get_rate_max: %s\n", snd_strerror(err)); return -1; } printf("max: (%c) %u\n", "<=>"[dir + 1], val);
snd_pcm_close(handle); return 0; }
On 10/08/2007 12:07 AM, Rene Herman wrote:
On 10/07/2007 07:04 PM, Peteris Krisjanis wrote:
So you suggest to open device via any means and then read that file? I did this and it works. I guess I can even try to implement it in app and check for sample rate everytime before I play.
Oh, I only notice this now by the way. If you are actually going to play, I believe you should just try and set the params you want and see if it works (or test them first, through things like snd_pcm_hw_params_test_rate().
Rene.
Introducing a /proc/asound/card0/pcm0{p,c}/hw_limits or similar might not be a bad idea though?
That would be good idea, with completion with 'alsainfo' about device.
How to do that properly, any clues before I try to sink my teeths in ALSA code?
P.
On Mon, 8 Oct 2007, Peteris Krisjanis wrote:
Introducing a /proc/asound/card0/pcm0{p,c}/hw_limits or similar might not be a bad idea though?
That would be good idea, with completion with 'alsainfo' about device.
How to do that properly, any clues before I try to sink my teeths in ALSA code?
Is this even possible? Ie, do sound cards actually tell you what their max or min hardware capabilities are and what info might you put into here? Ie, some cards can do 2 or 4 or 5.1 channel output. Do you put that in? And re speed, does the card actually advertise what speeds it supports, or is it more a matter of trying and if it does not support it, it rejects it.
On 10/08/2007 09:14 PM, Bill Unruh wrote:
On Mon, 8 Oct 2007, Peteris Krisjanis wrote:
Introducing a /proc/asound/card0/pcm0{p,c}/hw_limits or similar might not be a bad idea though?
That would be good idea, with completion with 'alsainfo' about device.
How to do that properly, any clues before I try to sink my teeths in ALSA code?
Is this even possible? Ie, do sound cards actually tell you what their max or min hardware capabilities are and what info might you put into here? Ie, some cards can do 2 or 4 or 5.1 channel output. Do you put that in? And re speed, does the card actually advertise what speeds it supports, or is it more a matter of trying and if it does not support it, it rejects it.
It's really only about communicating what the _driver_ accepts (ie, tells us the hardware can do). That is, basically export the snd_pcm_hardware struct for the stream.
It could also be done in userspace by opening the stream and quering the format_mask, channels and rates and stuff and I'll look into that a bit as a followup to the rates_min/max thing that I posted if only as a template thingy, but unless I'm missing it, I don't believe there's actually a way to get the information from userspace without opening the "hw" device, and that one only allows "subdevices_count" concurrent opens, meaning you'd have to stop things talking to your soundcard if it has just one (normal).
Exposing it directly in /proc/asound would be the way. Peteris; if you'll be looking, note that rates (and channels) might not be continuous between their min and max or anything; mostly it's enumerated stuff. Looking at the /proc/asound/card0/pcm0p/sub0/hw_params file generation probably helps (but that just exports the currently selected paramters, not all possible).
Rene.
On 10/08/2007 09:30 PM, Rene Herman wrote:
On 10/08/2007 09:14 PM, Bill Unruh wrote:
Introducing a /proc/asound/card0/pcm0{p,c}/hw_limits or similar might not be a bad idea though?
[ ... ]
It could also be done in userspace by opening the stream and quering the format_mask, channels and rates and stuff and I'll look into that a bit as a followup to the rates_min/max thing that I posted if only as a template thingy, but unless I'm missing it, I don't believe there's actually a way to get the information from userspace without opening the "hw" device, and that one only allows "subdevices_count" concurrent opens, meaning you'd have to stop things talking to your soundcard if it has just one (normal).
Okay, well, in userspace it would be something like this.
Not too sure about that subformat thing by the way. Jaroslav (or anyone, obviously): is the subformat intended/designed to be format-specific or snd_pcm_t global?
Currently there is only ever one subformat (STD) anyway but generally, I'd expect a subformat to be specific to a given format. There's just one non format-specific SND_PCM_SUBFORMAT_LAST though, no defines (or functions) to get subformats from the format. Is the design that all formats would be supporting all subformats or something?
This is modelled after the hw_params file in an opened substream directory (and ignores the possibility of anything more involved than simple min-max intervals). Provide a "-c" for the capture direction.
Not sure if this helps much (other than hopefully teaching me about subformats).
Rene
/* gcc -W -Wall -o ainfo ainfo.c -lasound */
#include <stdio.h> #include <unistd.h> #include <alsa/asoundlib.h>
#define DEVICE "hw:0,0" #define STREAM SND_PCM_STREAM_PLAYBACK
void snd_perror(const char *s, int err) { fprintf(stderr, "%s: %s\n", s, snd_strerror(err)); }
int hw_params_access(snd_pcm_hw_params_t *params) { snd_pcm_access_mask_t *mask; snd_pcm_access_t val; int err;
err = snd_pcm_access_mask_malloc(&mask); if (err < 0) { snd_perror("snd_pcm_acess_mask_malloc", err); return err; } snd_pcm_hw_params_get_access_mask(params, mask);
printf("access:"); for (val = 0; val <= SND_PCM_ACCESS_LAST; val++) if (snd_pcm_access_mask_test(mask, val)) printf(" %s", snd_pcm_access_name(val)); printf("\n");
snd_pcm_access_mask_free(mask); return 0; }
int hw_params_format(snd_pcm_hw_params_t *params) { snd_pcm_format_mask_t *mask; snd_pcm_format_t val; int err;
err = snd_pcm_format_mask_malloc(&mask); if (err < 0) { snd_perror("snd_pcm_format_mask_malloc", err); return err; } snd_pcm_hw_params_get_format_mask(params, mask);
printf("format:"); for (val = 0; val <= SND_PCM_FORMAT_LAST; val++) if (snd_pcm_format_mask_test(mask, val)) printf(" %s", snd_pcm_format_name(val)); printf("\n");
snd_pcm_format_mask_free(mask); return 0; }
int hw_params_subformat(snd_pcm_hw_params_t *params) { snd_pcm_subformat_mask_t *mask; snd_pcm_subformat_t val; int err;
err = snd_pcm_subformat_mask_malloc(&mask); if (err < 0) { snd_perror("snd_pcm_subformat_mask_malloc", err); return err; } snd_pcm_hw_params_get_subformat_mask(params, mask);
printf("subformat:"); for (val = 0; val <= SND_PCM_SUBFORMAT_LAST; val++) if (snd_pcm_subformat_mask_test(mask, val)) printf(" %s", snd_pcm_subformat_name(val)); printf("\n");
snd_pcm_subformat_mask_free(mask); return 0; }
int hw_params_channels(snd_pcm_hw_params_t *params) { unsigned int min; unsigned int max; int err;
err = snd_pcm_hw_params_get_channels_min(params, &min); if (err < 0) { snd_perror("snd_pcm_hw_params_get_channels_min", err); return err; } err = snd_pcm_hw_params_get_channels_max(params, &max); if (err < 0) { snd_perror("snd_pcm_hw_params_get_channels_max", err); return err; } printf("channels: %u-%u\n", min, max); return 0; }
int hw_params_rate(snd_pcm_hw_params_t *params) { unsigned int min; unsigned int max; int dir; int err;
err = snd_pcm_hw_params_get_rate_min(params, &min, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_rate_min", err); return err; } err = snd_pcm_hw_params_get_rate_max(params, &max, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_rate_max", err); return err; } printf("rate: %u-%u\n", min, max); return 0; }
int hw_params_period_size(snd_pcm_hw_params_t *params) { snd_pcm_uframes_t min; snd_pcm_uframes_t max; int dir; int err;
err = snd_pcm_hw_params_get_period_size_min(params, &min, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_period_size_min", err); return err; } err = snd_pcm_hw_params_get_period_size_max(params, &max, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_period_size_max", err); return err; } printf("period_size: %lu-%lu\n", min, max); return 0; }
int hw_params_buffer_size(snd_pcm_hw_params_t *params) { snd_pcm_uframes_t min; snd_pcm_uframes_t max; int err;
err = snd_pcm_hw_params_get_buffer_size_min(params, &min); if (err < 0) { snd_perror("snd_pcm_hw_params_get_buffer_size_min", err); return err; } err = snd_pcm_hw_params_get_buffer_size_max(params, &max); if (err < 0) { snd_perror("snd_pcm_hw_params_get_buffer_size_max", err); return err; } printf("buffer_size: %lu-%lu\n", min, max); return 0; }
int hw_params_tick_time(snd_pcm_hw_params_t *params) { unsigned int min; unsigned int max; int dir; int err;
err = snd_pcm_hw_params_get_tick_time_min(params, &min, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_tick_time_min", err); return err; } err = snd_pcm_hw_params_get_tick_time_max(params, &max, &dir); if (err < 0) { snd_perror("snd_pcm_hw_params_get_tick_time_max", err); return err; } printf("tick_time: %u-%u\n", min, max); return 0; }
int hw_params(snd_pcm_t *handle) { snd_pcm_hw_params_t *params; int err;
err = snd_pcm_hw_params_malloc(¶ms); if (err < 0) { snd_perror("snd_pcm_hw_params_alloc", err); return err; } err = snd_pcm_hw_params_any(handle, params); if (err < 0) { snd_perror("snd_pcm_hw_params_any", err); return err; } err = snd_pcm_hw_params_set_rate_resample(handle, params, 0); if (err < 0) { snd_perror("snd_pcm_hw_params_set_resample", err); return err; }
err = hw_params_access(params); if (err < 0) return err;
err = hw_params_format(params); if (err < 0) return err;
err = hw_params_subformat(params); if (err < 0) return err;
err = hw_params_channels(params); if (err < 0) return err;
err = hw_params_rate(params); if (err < 0) return err;
err = hw_params_period_size(params); if (err < 0) return err;
err = hw_params_buffer_size(params); if (err < 0) return err;
err = hw_params_tick_time(params); if (err < 0) return err;
snd_pcm_hw_params_free(params); return 0; }
int main(int argc, char *argv[]) { const char *device = DEVICE; snd_pcm_stream_t stream = STREAM;
snd_pcm_t *handle; int err;
while ((err = getopt(argc, argv, "pc")) != -1) switch (err) { case 'p': stream = SND_PCM_STREAM_PLAYBACK; break; case 'c': stream = SND_PCM_STREAM_CAPTURE; break; case '?': return -1; }
if (optind < argc) device = argv[optind];
err = snd_pcm_open(&handle, device, stream, 0); if (err < 0) { snd_perror("snd_pcm_open", err); return err; }
err = hw_params(handle); if (err < 0) return err; err = snd_pcm_close(handle); if (err < 0) { snd_perror("snd_pcm_close", err); return err; }
return 0; }
On 10/09/2007 02:23 AM, Rene Herman wrote:
Not too sure about that subformat thing by the way. Jaroslav (or anyone, obviously): is the subformat intended/designed to be format-specific or snd_pcm_t global?
Currently there is only ever one subformat (STD) anyway but generally, I'd expect a subformat to be specific to a given format. There's just one non format-specific SND_PCM_SUBFORMAT_LAST though, no defines (or functions) to get subformats from the format. Is the design that all formats would be supporting all subformats or something?
Takashi, with Jaroslav his usual blackhole self -- do you know what the idea is of the subformats?
Rene.
At Mon, 15 Oct 2007 22:20:10 +0200, Rene Herman wrote:
On 10/09/2007 02:23 AM, Rene Herman wrote:
Not too sure about that subformat thing by the way. Jaroslav (or anyone, obviously): is the subformat intended/designed to be format-specific or snd_pcm_t global?
Currently there is only ever one subformat (STD) anyway but generally, I'd expect a subformat to be specific to a given format. There's just one non format-specific SND_PCM_SUBFORMAT_LAST though, no defines (or functions) to get subformats from the format. Is the design that all formats would be supporting all subformats or something?
Takashi, with Jaroslav his usual blackhole self -- do you know what the idea is of the subformats?
I'm also not 100% sure, and I think this hasn't been much discussed until now. That's why we have no definition yet.
As my understanding, it could be both. The sub-format is in general an additional attribute. An attribute can be generic for all formats and can be specific for certain formats.
IMO, we can drop this field now (but keep it as a place holder for compatibility).
Takashi
participants (4)
-
Bill Unruh
-
Peteris Krisjanis
-
Rene Herman
-
Takashi Iwai