-
-
Save ghedo/963382 to your computer and use it in GitHub Desktop.
/* | |
* Simple sound playback using ALSA API and libasound. | |
* | |
* Compile: | |
* $ cc -o play sound_playback.c -lasound | |
* | |
* Usage: | |
* $ ./play <sample_rate> <channels> <seconds> < <file> | |
* | |
* Examples: | |
* $ ./play 44100 2 5 < /dev/urandom | |
* $ ./play 22050 1 8 < /path/to/file.wav | |
* | |
* Copyright (C) 2009 Alessandro Ghedini <alessandro@ghedini.me> | |
* -------------------------------------------------------------- | |
* "THE BEER-WARE LICENSE" (Revision 42): | |
* Alessandro Ghedini wrote this file. As long as you retain this | |
* notice you can do whatever you want with this stuff. If we | |
* meet some day, and you think this stuff is worth it, you can | |
* buy me a beer in return. | |
* -------------------------------------------------------------- | |
*/ | |
#include <alsa/asoundlib.h> | |
#include <stdio.h> | |
#define PCM_DEVICE "default" | |
int main(int argc, char **argv) { | |
unsigned int pcm, tmp, dir; | |
int rate, channels, seconds; | |
snd_pcm_t *pcm_handle; | |
snd_pcm_hw_params_t *params; | |
snd_pcm_uframes_t frames; | |
char *buff; | |
int buff_size, loops; | |
if (argc < 4) { | |
printf("Usage: %s <sample_rate> <channels> <seconds>\n", | |
argv[0]); | |
return -1; | |
} | |
rate = atoi(argv[1]); | |
channels = atoi(argv[2]); | |
seconds = atoi(argv[3]); | |
/* Open the PCM device in playback mode */ | |
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, | |
SND_PCM_STREAM_PLAYBACK, 0) < 0) | |
printf("ERROR: Can't open \"%s\" PCM device. %s\n", | |
PCM_DEVICE, snd_strerror(pcm)); | |
/* Allocate parameters object and fill it with default values*/ | |
snd_pcm_hw_params_alloca(¶ms); | |
snd_pcm_hw_params_any(pcm_handle, params); | |
/* Set parameters */ | |
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params, | |
SND_PCM_ACCESS_RW_INTERLEAVED) < 0) | |
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params, | |
SND_PCM_FORMAT_S16_LE) < 0) | |
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0) | |
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0) | |
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm)); | |
/* Write parameters */ | |
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0) | |
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm)); | |
/* Resume information */ | |
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle)); | |
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle))); | |
snd_pcm_hw_params_get_channels(params, &tmp); | |
printf("channels: %i ", tmp); | |
if (tmp == 1) | |
printf("(mono)\n"); | |
else if (tmp == 2) | |
printf("(stereo)\n"); | |
snd_pcm_hw_params_get_rate(params, &tmp, 0); | |
printf("rate: %d bps\n", tmp); | |
printf("seconds: %d\n", seconds); | |
/* Allocate buffer to hold single period */ | |
snd_pcm_hw_params_get_period_size(params, &frames, 0); | |
buff_size = frames * channels * 2 /* 2 -> sample size */; | |
buff = (char *) malloc(buff_size); | |
snd_pcm_hw_params_get_period_time(params, &tmp, NULL); | |
for (loops = (seconds * 1000000) / tmp; loops > 0; loops--) { | |
if (pcm = read(0, buff, buff_size) == 0) { | |
printf("Early end of file.\n"); | |
return 0; | |
} | |
if (pcm = snd_pcm_writei(pcm_handle, buff, frames) == -EPIPE) { | |
printf("XRUN.\n"); | |
snd_pcm_prepare(pcm_handle); | |
} else if (pcm < 0) { | |
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm)); | |
} | |
} | |
snd_pcm_drain(pcm_handle); | |
snd_pcm_close(pcm_handle); | |
free(buff); | |
return 0; | |
} |
@rsingh2083 The click sound is probably because it plays the metadata of the wav file. In a real player you would need to skip the header of the wav file.
How to play 24-bit audio?
How to play 24-bit audio?
You can use SND_PCM_FORMAT_S24_LE instead of SND_PCM_FORMAT_S16_LE
It's nice to see an attempt at a minimal ALSA program. There are some bugs that should probably be cleaned up, though. Examples:
-
&rate
should be justrate
, -
variable
pcm
should be callederr
as in the ALSA docs, -
the audio file should be played to the end instead of for a certain number of seconds,
-
buff_size should not presume sample width is 2 bytes. For calculating buff_size, one can use
_physical_width
snd_pcm_format_t sampfmt; snd_pcm_hw_params_get_format(params, &sampfmt); printf("sample format: %s\n", snd_pcm_format_description(sampfmt)); snd_pcm_format_t sampwidth = snd_pcm_format_physical_width(sampfmt); printf("sample width: %d bits\n", sampwidth);
Hey! Thanks for sharing. It does run flawless but i'm running into memory corruption when wrapping logic into functions. I probably am thinking about it wrong but i don't see a problem with my code, but i want to rule out ALSA is doing something odd. If anyone could take a look it would be greatly appreciated.
https://gist.github.com/dedobbin/29adb1dae10932b4a88721bb00a1fc45
edit: oh, already found it. very silly, i used snd_pcm_hw_params_alloca which allocated on the stack obviously
should have used snd_pcm_hw_params_malloc
I have modified the code to play wav files instead of stdin, I find that the wav files are played at a faster tempo. The problem is described here https://stackoverflow.com/questions/77920231/wav-files-are-played-by-alsa-at-a-faster-tempo-is-there-a-way-to-fix-the-tempo
Could anyone kindly point me my mistake?
./play 44100 2 2 < hello.wav
Just gives out a very short click sound and then Im getting early end of file. Can anyone help ?