-
-
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 <[email protected]> | |
* -------------------------------------------------------------- | |
* "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; | |
} |
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 ?
pi@raspberrypi:~/tinyalsa $ ./play 44100 2 2 < hello.wav PCM name: 'default' PCM state: PREPARED channels: 2 (stereo) rate: 44100 bps seconds: 2 Early end of file.
I know I'm extremely late but since no one has replied to you, I figured I could at least answer this qn.
The reason no sound is played when it finishes reading early is because we return as well after finish reading, which destroys everything. Whereas what we really want is just to break out of the for loop and stop reading and writing.
So arnd line 108, inside the if(pcm = read(0...))
Replace return 0; to break;
Oh I just realised someone else alr found the issue but it was commented b4 you
Я змінив код для відтворення файлів wav замість stdin, я виявив, що файли wav відтворюються у швидшому темпі. Проблема описана тут https://stackoverflow.com/questions/77920231/wav-files-are-played-by-alsa-at-a-faster-tempo-is-there-a-way-to-fix-the -темп
Хтось може вказати мені на мою помилку?
Your code is fine and it works fine for me, so I can conclude that something is wrong with the program startup parameters.
For your wav file, the startup command should be as follows:
./<program name> 22050 1 30 PinkPanther30.wav default
Edit:
I would recommend that you do not skip the first 44 bytes of the metadata. Some files have more than 44 bytes of metadata if there is some additional information. You need to search for the keyword “data” + 4 bytes and after that only the audio data comes.
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