Reputation: 150
I am trying to play this sound using the program below, but the sound is a little fast and it skips. The sound file's sample rate is 11025 Hz, stereo, the sample size is 16 bit. The issue appears to be snd_pcm_hw_params_set_buffer_size()
and snd_pcm_hw_params_set_period_size()
(both are functions I am studying) due to invalid arguments, but I don't know why they are invalid and commenting them out doesn't address the skipping effect. What am I doing wrong?
#include <stdlib.h>
#include <stdint.h>
#include <alsa/asoundlib.h>
#define STEREO 2
#define BITS 16 / 8
#define FRAMEBUFFERSIZE 512 // in samples
#define PERIODS 2
#define SAMPLERATE 11025
void snd_error_checker(int error, char *function_name)
{
if (error)
{
printf("Error (%s): %s\n", function_name, snd_strerror(error));
// exit(EXIT_FAILURE);
}
}
int main(void)
{
snd_pcm_t *handle;
uint32_t channels = STEREO;
uint32_t sample_size = BITS; // 16 bit
uint32_t frame_size = sample_size * channels;
snd_pcm_uframes_t frames = FRAMEBUFFERSIZE / frame_size;
snd_pcm_uframes_t frames_per_period = frames / PERIODS;
int32_t dir = 0; // No idea what this does.
snd_pcm_hw_params_t *params;
int16_t *buffer;
FILE *wav;
int32_t size;
int error;
error = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
snd_error_checker(error, "snd_pcm_open()");
snd_pcm_hw_params_alloca(¶ms);
error = snd_pcm_hw_params_any(handle, params);
snd_error_checker(error, "snd_pcm_hw_params_any()");
error = snd_pcm_hw_params_set_buffer_size(handle, params, frames);
snd_error_checker(error, "snd_pcm_hw_params_set_buffer_size()");
error = snd_pcm_hw_params_set_period_size(handle, params, frames_per_period, dir);
snd_error_checker(error, "snd_pcm_hw_params_set_period_size()");
error = snd_pcm_hw_params_set_rate(handle, params, SAMPLERATE, dir);
snd_error_checker(error, "snd_pcm_hw_params_set_rate()");
error = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_error_checker(error, "snd_pcm_hw_params_set_access()");
error = snd_pcm_hw_params_set_channels(handle, params, STEREO);
snd_error_checker(error, "snd_pcm_hw_params_set_channels()");
error = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
snd_error_checker(error, "snd_pcm_hw_params_set_format()");
error = snd_pcm_hw_params(handle, params);
snd_error_checker(error, "snd_pcm_hw_params()");
// Insert samples to framebuffer.
wav = fopen("pcm1611s.wav", "r");
fseek(wav, 0L, SEEK_END);
size = ftell(wav);
fseek(wav, 0L, SEEK_SET);
buffer = malloc(size);
fread(buffer, sizeof(int16_t), size, wav);
fclose(wav);
size /= sizeof(int16_t);
// Set ptrbuffer 46 bytes ahead to skip the header.
for (int16_t *ptrbuffer = buffer + 46; size > ptrbuffer - buffer; ptrbuffer += FRAMEBUFFERSIZE * STEREO * BITS)
{
error = snd_pcm_writei(handle, ptrbuffer, frames);
if (error)
{
snd_pcm_recover(handle, error, 0);
}
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
exit(EXIT_SUCCESS);
}
Upvotes: 0
Views: 331
Reputation: 180020
#define FRAMEBUFFERSIZE 512 // in samples
The comment is lying; the value is used as a byte count.
uint32_t sample_size = BITS; // 16 bit
The comment is lying; the value is actualy measured in bytes. (And BITS
is named wrong.)
fread(buffer, sizeof(int16_t), size, wav);
size
is measured in bytes, but you tell fread()
to read 16-bit words. (This does not actually hurt because it stops reading at the end of the file. But you should have checked for errors.)
int16_t *ptrbuffer = ...
You are telling the compiler that ptrbuffer
points to 16-bit values.
ptrbuffer += FRAMEBUFFERSIZE * STEREO * BITS
You are telling the compiler to step over 2048 16-bit values, i.e., over 4096 bytes. You actually want to step over all the sample in a buffer, that is, frames * STEREO
.
error = snd_pcm_writei(...);
if (error)
snd_pcm_writei()
returns a positive number on success; errors are negative.
Upvotes: 1
Reputation: 461
Three points need to be consider for proper playback
Check return value of snd_pcm_writei
, will give a clear idea , whats going wrong if any error. Referalsa-project
Reading file in while()
loop and giving to snd_pcm_writei
can cause skip in audio with under run (-EPIPE
error), since fread
is time consuming call.
Check return values of all of your library calls and make sure all are success.
Also make sure the values are setting properly in driver . For example, after snd_pcm_hw_params_set_rate
verify it with snd_pcm_hw_params_get_rate
, if your driver is not supporting sampling rate 11025KHz, we can find out it with return values and rechecking with snd_pcm_hw_params_get_rate
Upvotes: 1