Monday, June 22, 2015

ALSA playback in Linux

@Readme
# build mode
release : make
debug   : make debug

# clean
make clean


@Makefile
SRC=alsa_playback.c
TARGET=playback
LIB=asound

target:
gcc -o${TARGET} ${SRC} -l${LIB}

debug:
gcc -DDEBUG -o${TARGET} ${SRC} -l${LIB}

clean:
rm -rf ${TARGET}


@ source
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

#include <stdio.h>
#include <stdint.h>

#define BUF_SIZE (30*16000)
#define WR_SIZE (2048) // Why 2048 ???

void SetParameters(snd_pcm_t* handle, snd_pcm_hw_params_t* params);
void Playback(snd_pcm_t* handle, const char* sndfile);

int set_hw_params(snd_pcm_t* handle, snd_pcm_hw_params_t* params);
void get_hw_params(snd_pcm_t* handle, snd_pcm_hw_params_t* params);

int LoadSoundfile(const char* filename, uint8_t* buffer);


int main(int argc, char* argv[])
{
int err;
snd_pcm_t* handle;
snd_pcm_hw_params_t* params;

if (argc != 2)
{
fprintf(stderr, "Usage: ./playback [input_snd_file]\n");
exit(EXIT_FAILURE);
}

/* Open PCM device for playback */
if ((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
fprintf(stderr, "snd_pcm_open failed.\n");
exit(EXIT_FAILURE);
}

/* Allocate a hardware parameters object */
snd_pcm_hw_params_alloca(&params);

/* Setting Parameters */
SetParameters(handle, params);

/* Simple sound playback */
Playback(handle, argv[1]);

/* Close PCM device */
snd_pcm_close(handle);

return 0;
}


void Playback(snd_pcm_t* handle, const char* sndfile)
{
int err;
int16_t readBuffer[BUF_SIZE] = {0,};
int16_t* offset = readBuffer;
int fileSize=0, readSize=0;

/* Load a SoundFile */
fileSize = LoadSoundfile(sndfile, (uint8_t*)readBuffer);

/* Playback Loop until EOF */
while (offset - readBuffer <= fileSize)
{
int nRead = snd_pcm_writei(handle, offset, WR_SIZE);

#ifdef DEBUG
fprintf(stderr, "nRead(%d) => offsetSize(%d) / totalSize(%d)\n"
, nRead, offset - readBuffer, fileSize);
#endif

if (nRead == 0)
{
fprintf(stderr, "End of file on input\n");
}
else if (nRead == -EPIPE)
{
fprintf(stderr, "Underrun occurred.\n");
snd_pcm_prepare(handle);
nRead = 0;
}
else if (nRead < 0)
{
fprintf(stderr, "Read error.\n");
exit(EXIT_FAILURE);
}

usleep(10000);
offset += nRead;
}

}


int LoadSoundfile(const char* filename, uint8_t* buffer)
{
int nFileSize;
FILE* fp = fopen(filename, "r");

memset(buffer, 0, BUF_SIZE);
nFileSize = fread((int8_t*)buffer, sizeof(int16_t), BUF_SIZE, fp);
printf(" -> FileSize = %d\n", nFileSize);

return nFileSize;
}


void SetParameters(snd_pcm_t* handle, snd_pcm_hw_params_t* params)
{
int err;

/* Configure HW parameters */
if ((err = set_hw_params(handle, params)) < 0)
{
fprintf(stderr, "set_hw_params failed: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}

#ifdef DEBUG
/* Display information about the PCM interface */
get_hw_params(handle, params);
#endif
}


int set_hw_params(snd_pcm_t* handle, snd_pcm_hw_params_t* params)
{
int err;
int channels;
int rate, direction, target;
snd_pcm_uframes_t period_size;
int periods, dir;

printf("*** Set pcm hw parameters ***\n");

/* Fill it in with default values */
if ((err = snd_pcm_hw_params_any(handle, params)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_any failed.\n");
return err;
}

/* Read/Write Interleaved mode */
if ((err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_access failed.\n");
return err;
}

/* Signed 16-bit little-endian format */
if ((err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_format failed.\n");
return err;
}

/* Mono channel, 1: mono, 2: stereo */
channels = 1;
if ((err = snd_pcm_hw_params_set_channels(handle, params, channels)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_channels failed.\n");
exit(EXIT_FAILURE);
}

/* 16 kbps sampling rate */
target = rate = 16000;
if ((err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_rate_near failed.\n");
return err;
}

/* 128 frames per period */
period_size = 128;
if ((err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, &dir)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_period_size_near failed.\n");
return err;
}

/* 16 periods per buffer */
periods = 16;
if ((err = snd_pcm_hw_params_set_periods_near(handle, params, &periods, &dir)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params_set_periods_near failed.\n");
return err;
}

/* Write the parameters to the driver */
if ((err = snd_pcm_hw_params(handle, params)) < 0)
{
fprintf(stderr, "snd_pcm_hw_params failed.\n");
return err;
}

return 0;
}


void get_hw_params(snd_pcm_t* handle, snd_pcm_hw_params_t* params)
{
int val;
int dir;
snd_pcm_uframes_t frames;

printf("\n*** Display information about the PCM interface ***\n");

printf("  ALSA library version: %s\n", SND_LIB_VERSION_STR);

printf("  PCM handle name = %s\n", snd_pcm_name(handle));
printf("  PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(handle)));

snd_pcm_hw_params_get_access(params, (snd_pcm_access_t*)&val);
printf("  Access type = %s\n", snd_pcm_access_name((snd_pcm_access_t)val));

snd_pcm_hw_params_get_format(params, (snd_pcm_format_t*)&val);
printf("  Fotmat = %s ", snd_pcm_format_name((snd_pcm_format_t)val));
printf("(%s)\n", snd_pcm_format_description((snd_pcm_format_t)val));

snd_pcm_hw_params_get_subformat(params, (snd_pcm_subformat_t*)&val);
printf("  Subformat = %s ", snd_pcm_subformat_name((snd_pcm_subformat_t)val));
printf("(%s)\n", snd_pcm_subformat_description((snd_pcm_subformat_t)val));

snd_pcm_hw_params_get_channels(params, &val);
printf("  Channels = %d\n", val);

snd_pcm_hw_params_get_rate(params, &val, &dir);
printf("  Sampling rate = %d\n", val);

snd_pcm_hw_params_get_period_time(params, &val, &dir);
printf("  Period time = %d us (= %d ms)\n", val, val/1000);

snd_pcm_hw_params_get_period_size(params, &frames, &dir);
printf("  Period size = %d frames\n", (int)frames);

snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
printf("  Buffer time = %d us (= %d ms)\n", val, val/1000);

snd_pcm_hw_params_get_buffer_size(params, &frames);
printf("  Buffer size = %d frames\n", (int)frames);

snd_pcm_hw_params_get_periods(params, &val, &dir);
printf("  Periods per buffer = %d periods\n", val);

printf("\n");
}

No comments:

Post a Comment