//File : snd.c
//Author : Loon <sepnic@gmail.com>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <alsa/asoundlib.h>
#include "snd.h"
typedef struct SND_Container {
snd_pcm_t *handle;
snd_output_t *log;
snd_pcm_uframes_t chunk_size;
snd_pcm_uframes_t buffer_size;
snd_pcm_format_t format;
uint16_t channels;
size_t chunk_bytes;
size_t bits_per_sample;
size_t bits_per_frame;
uint8_t *data_buf;
} SND_Container_t;
static SND_Container_t playback;
static SND_Container_t capture;
////////////////////////// private function //////////////////////////////
ssize_t SND_readPcm(SND_Container_t *snd, uint8_t * buffer, size_t rcount)
{
ssize_t r;
size_t result = 0;
size_t count = rcount;
uint8_t *data = buffer;
if (count != snd->chunk_size) {
count = snd->chunk_size;
}
while (count > 0) {
r = snd_pcm_readi(snd->handle, data, count);
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
snd_pcm_wait(snd->handle, 1000);
} else if (r == -EPIPE) {
snd_pcm_prepare(snd->handle);
fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Overrun >>>>>>>>>>>>>>>\n");
} else if (r == -ESTRPIPE) {
fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>\n");
} else if (r < 0) {
fprintf(stderr, "Error snd_pcm_writei: [%s]", snd_strerror(r));
return -1;
}
if (r > 0) {
result += r;
count -= r;
data += r * snd->bits_per_frame / 8;
}
}
return rcount;
}
ssize_t SND_writePcm(SND_Container_t *snd, uint8_t * buffer, size_t wcount)
{
ssize_t r;
ssize_t result = 0;
uint8_t *data = buffer;
if (wcount < snd->chunk_size) {
snd_pcm_format_set_silence(snd->format,
data + wcount * snd->bits_per_frame / 8,
(snd->chunk_size - wcount) * snd->channels);
wcount = snd->chunk_size;
}
while (wcount > 0) {
r = snd_pcm_writei(snd->handle, data, wcount);
if (r == -EAGAIN || (r >= 0 && (size_t)r < wcount)) {
snd_pcm_wait(snd->handle, 1000);
} else if (r == -EPIPE) {
snd_pcm_prepare(snd->handle);
fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n");
} else if (r == -ESTRPIPE) {
fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>\n");
} else if (r < 0) {
fprintf(stderr, "Error snd_pcm_writei: [%s]", snd_strerror(r));
return -1;
}
if (r > 0) {
result += r;
wcount -= r;
data += r * snd->bits_per_frame / 8;
}
}
return result;
}
snd_pcm_format_t SND_P_getFormat(int sample_length)
{
switch (sample_length) {
case 16:
return SND_PCM_FORMAT_S16_LE;
break;
case 8:
return SND_PCM_FORMAT_U8;
break;
default:
return SND_PCM_FORMAT_UNKNOWN;
break;
}
return SND_PCM_FORMAT_UNKNOWN;
}
int SND_setParams(SND_Container_t *snd, int channels, int sample_rate, int sample_length)
{
snd_pcm_hw_params_t *hwparams;
uint32_t exact_rate;
uint32_t buffer_time, period_time;
snd_pcm_format_t format = SND_P_getFormat(sample_length);
/* Allocate the snd_pcm_hw_params_t structure on the stack. */
snd_pcm_hw_params_alloca(&hwparams);
/* Init hwparams with full configuration space */
if (snd_pcm_hw_params_any(snd->handle, hwparams) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_any\n");
goto err;
}
if (snd_pcm_hw_params_set_access(snd->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_access\n");
goto err;
}
/* Set sample format */
if (snd_pcm_hw_params_set_format(snd->handle, hwparams, format) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_format\n");
goto err;
}
snd->format = format;
/* Set number of channels */
if (snd_pcm_hw_params_set_channels(snd->handle, hwparams, channels) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_channels\n");
goto err;
}
snd->channels = channels;
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = sample_rate;
if (snd_pcm_hw_params_set_rate_near(snd->handle, hwparams, &exact_rate, 0) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_rate_near\n");
goto err;
}
if (sample_rate != exact_rate) {
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n ==> Using %d Hz instead.\n",
sample_rate, exact_rate);
}
if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_get_buffer_time_max\n");
goto err;
}
if (buffer_time > 500000) buffer_time = 500000;
period_time = buffer_time / 4;
if (snd_pcm_hw_params_set_buffer_time_near(snd->handle, hwparams, &buffer_time, 0) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_buffer_time_near\n");
goto err;
}
if (snd_pcm_hw_params_set_period_time_near(snd->handle, hwparams, &period_time, 0) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params_set_period_time_near\n");
goto err;
}
/* Set hw params */
if (snd_pcm_hw_params(snd->handle, hwparams) < 0) {
fprintf(stderr, "Error snd_pcm_hw_params(handle, params)\n");
goto err;
}
snd_pcm_hw_params_get_period_size(hwparams, &snd->chunk_size, 0);
snd_pcm_hw_params_get_buffer_size(hwparams, &snd->buffer_size);
if (snd->chunk_size == snd->buffer_size) {
fprintf(stderr, ("Can't use period equal to buffer size (%lu == %lu)\n"), snd->chunk_size, snd->buffer_size);
goto err;
}
snd->bits_per_sample = snd_pcm_format_physical_width(snd->format);
snd->bits_per_frame = snd->bits_per_sample * snd->channels;
snd->chunk_bytes = snd->chunk_size * snd->bits_per_frame / 8;
/* Allocate audio data buffer */
snd->data_buf = (uint8_t *)malloc(snd->chunk_bytes);
if (!snd->data_buf) {
fprintf(stderr, "Error malloc: [data_buf]\n");
goto err;
}
return 0;
err:
return -1;
}
ssize_t SND_P_saveRead(int fd, void *buf, size_t count)
{
ssize_t result = 0, ret;
while (count > 0) {
if ((ret = read(fd, buf, count)) == 0)
break;
if (ret < 0)
return result > 0 ? result : ret;
count -= ret;
result += ret;
buf = (char *)buf + ret;
}
return result;
}
////////////////////////// public function //////////////////////////////
int snd_open(snd_stream_type_t stream)
{
const char *devicename = "default";
snd_pcm_stream_t direction =
stream == SND_STREAM_PLAYBACK ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
SND_Container_t *snd =
stream == SND_STREAM_PLAYBACK ? &playback : &capture;
if (snd_output_stdio_attach(&snd->log, stderr, 0) < 0) {
fprintf(stderr, "Error snd_output_stdio_attach\n");
goto err;
}
if (snd_pcm_open(&snd->handle, devicename, direction, 0) < 0) {
fprintf(stderr, "Error snd_pcm_open [ %s]\n", devicename);
goto err;
}
return 0;
err:
if (snd->data_buf) free(snd->data_buf);
if (snd->log) snd_output_close(snd->log);
if (snd->handle) snd_pcm_close(snd->handle);
return -1;
}
void snd_close(snd_stream_type_t stream)
{
SND_Container_t *snd =
stream == SND_STREAM_PLAYBACK ? &playback : &capture;
snd_pcm_drain(snd->handle);
free(snd->data_buf);
snd_output_close(snd->log);
snd_pcm_close(snd->handle);
}
int snd_set(snd_stream_type_t stream, int channels, int sample_rate, int sample_length)
{
SND_Container_t *snd =
stream == SND_STREAM_PLAYBACK ? &playback : &capture;
assert(snd->handle);
if (SND_setParams(snd, channels, sample_rate, sample_length) < 0) {
fprintf(stderr, "Error snd_set[channels:%d, sample_rate:%dhz, sample_length:%d]\n",
channels, sample_rate, sample_length);
return -1;
}
snd_pcm_dump(snd->handle, snd->log);
return 0;
}
int snd_playback(int fd)
{
int ret;
off64_t c = playback.chunk_bytes;
ret = SND_P_saveRead(fd, playback.data_buf, c);
if (ret <= 0) {
fprintf(stderr, "Error safe_read\n");
return -1;
}
// Transfer to size frame
ret = ret * 8 / playback.bits_per_frame;
if (SND_writePcm(&playback, playback.data_buf, ret) != ret) {
fprintf(stderr, "Error snd_write [SND_writePcm]\n");
return -1;
}
return 0;
}
int snd_playback_1(uint8_t *buffer, size_t length)
{
int write_bytes
评论10
最新资源