接口文件 record_interface.h
/**
* @file record_interface.h
* @brief For the operation of record API
*/
#ifndef __record_INTERFACE_HEAD__
#define __record_INTERFACE_HEAD__
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <locale.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "alsa/asoundlib.h"
#include <assert.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef struct {
/**
* @brief Alsa handle.
*/
snd_pcm_t *handle;
/**
* @brief Alsa log Output redirection.
*/
snd_output_t *log;
/**
* @brief Alsa internal audio cache buffer size.
*/
snd_pcm_uframes_t buffer_size;
/**
* @brief Data format.
*/
snd_pcm_format_t format;
/**
* @brief The number of channels.
*/
uint16_t channels;
/**
* @brief The number of bit per sample point.
*/
size_t bits_per_sample;
/**
* @brief The number of bit per frame.
*/
size_t bits_per_frame;
/**
* @brief The amount of frame required to record.
*/
snd_pcm_uframes_t chunk_size;
/**
* @brief The number of bytes required to be recorded is matched with the
* chunk_size.
*/
size_t chunk_bytes;
/**
* @brief Point to the recording data buffer, the size is chunk_bytes bytes,
* have chunk_size frame.
*/
uint8_t *data_buf;
} record_handle_t;
typedef struct {
/*
* Specifies data format.
* SND_PCM_FORMAT_S16_LE;
* SND_PCM_FORMAT_S8;
* SND_PCM_FORMAT_UNKNOWN;
* All references at sound/asound.h
*/
snd_pcm_format_t format;
/**
* @brief Specifies the number of channels.
*/
uint16_t channels;
/**
* @brief frequence of sample
*/
uint32_t sample_rate;
} record_params_t;
/**
* @brief Get the handle for the recording.
*
* @param record_params Recording parameters.
*
* @return Record handle on success otherwise NULL is returned.
*/
record_handle_t *mozart_get_record_handle(record_params_t record_params);
/**
* @brief Get the recording data, the size is chunk_bytes bytes.
*
* @param record_handle
*
* @return Return a pointer to data on success otherwise NULL is returned.
*/
uint8_t *mozart_record_get_data(record_handle_t * record_handle);
/**
* @brief Release record handle after call mozart_get_record_handle.
*
* @param record_handle
*/
void mozart_release_record_handle(record_handle_t * record_handle);
#endif /* __record_INTERFACE_HEAD__ */
实现文件 record.c
/**
* @file record.c
* @brief For the operation of record API
*/
#include "record_interface.h"
#define DEBUG(x,y...) {printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}
#define ERROR(x,y...) {printf("[ %s : %s : %d] ",__FILE__, __func__, __LINE__); printf(x,##y); printf("\n");}
static int record_set_params(record_handle_t * record_handle, record_params_t record_params)
{
snd_pcm_hw_params_t *hwparams;
uint32_t exact_rate;
uint32_t buffer_time, period_time;
/* 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(record_handle->handle, hwparams) < 0) {
ERROR("Error snd_pcm_hw_params_any");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_access(record_handle->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
ERROR("Error snd_pcm_hw_params_set_access");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_format(record_handle->handle, hwparams, record_params.format) < 0) {
ERROR("Error snd_pcm_hw_params_set_format");
goto ERR_SET_PARAMS;
}
record_handle->format = record_params.format;
/* Set number of channels */
if (snd_pcm_hw_params_set_channels(record_handle->handle, hwparams, record_params.channels) < 0) {
ERROR("Error snd_pcm_hw_params_set_channels");
goto ERR_SET_PARAMS;
}
record_handle->channels = record_params.channels;
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = record_params.sample_rate;
if (snd_pcm_hw_params_set_rate_near(record_handle->handle, hwparams, &exact_rate, 0) < 0) {
ERROR("Error snd_pcm_hw_params_set_rate_near");
goto ERR_SET_PARAMS;
}
if (record_params.sample_rate != exact_rate) {
ERROR("The rate %d Hz is not supported by your hardware. ==> Using %d Hz instead.",
record_params.sample_rate, exact_rate);
}
if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) {
ERROR("Error snd_pcm_hw_params_get_buffer_time_max");
goto ERR_SET_PARAMS;
}
if (buffer_time > 500000) buffer_time = 500000;
period_time = buffer_time / 4;
if (snd_pcm_hw_params_set_buffer_time_near(record_handle->handle, hwparams, &buffer_time, 0) < 0) {
ERROR("Error snd_pcm_hw_params_set_buffer_time_near");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_period_time_near(record_handle->handle, hwparams, &period_time, 0) < 0) {
ERROR("Error snd_pcm_hw_params_set_period_time_near");
goto ERR_SET_PARAMS;
}
/* Set hw params */
if (snd_pcm_hw_params(record_handle->handle, hwparams) < 0) {
ERROR("Error snd_pcm_hw_params(handle, params)");
goto ERR_SET_PARAMS;
}
snd_pcm_hw_params_get_period_size(hwparams, &record_handle->chunk_size, 0);
snd_pcm_hw_params_get_buffer_size(hwparams, &record_handle->buffer_size);
if (record_handle->chunk_size == record_handle->buffer_size) {
ERROR(("Can't use period equal to buffer size (%lu == %lu)"), record_handle->chunk_size, record_handle->buffer_size);
goto ERR_SET_PARAMS;
}
//通过fomart获取每sample的bit数
record_handle->bits_per_sample = snd_pcm_format_physical_width(record_params.format);
//把frame定义为一个sample的数据,包括每个channel
record_handle->bits_per_frame = record_handle->bits_per_sample * record_params.channels;
//计算录chunk_size个frame需要的byte数
record_handle->chunk_bytes = record_handle->chunk_size * record_handle->bits_per_frame / 8;
//开辟录音buffer
record_handle->data_buf = (uint8_t *)malloc(record_handle->chunk_bytes);
if (!record_handle->data_buf) {
ERROR("Error malloc: [data_buf]");
goto ERR_SET_PARAMS;
}
return 0;
ERR_SET_PARAMS:
return -1;
}
record_handle_t *mozart_get_record_handle(record_params_t record_params)
{
char *devicename = "default";
record_handle_t *record_handle;
record_handle = (record_handle_t *)malloc(sizeof(record_handle_t));
memset(record_handle, 0x0, sizeof(record_handle_t));
if (snd_output_stdio_attach(&record_handle->log, stderr, 0) < 0) {
ERROR("Error snd_output_stdio_attach");
free(record_handle);
return NULL;
}
if (snd_pcm_open(&(record_handle->handle), devicename, SND_PCM_STREAM_CAPTURE, 0) < 0) {
ERROR("Error snd_pcm_open [ %s]", devicename);
free(record_handle);
return NULL;
}
if (record_set_params(record_handle, record_params) < 0) {
ERROR("Error set_snd_pcm_params");
free(record_handle);
return NULL;
}
//snd_pcm_dump(record_handle->handle, record_handle->log);
return record_handle;
}
static ssize_t read_pcm(record_handle_t *record_handle)
{
ssize_t r;
size_t result = 0;
size_t count = record_handle->chunk_size;
uint8_t *data = record_handle->data_buf;
while (count > 0) {
//录count个frame到data中
r = snd_pcm_readi(record_handle->handle, data, count);
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
snd_pcm_wait(record_handle->handle, 1000);
} else if (r == -EPIPE) {
snd_pcm_prepare(record_handle->handle);
fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n");
} else if (r == -ESTRPIPE) {
fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>\n");
} else if (r < 0) {
fprintf(stderr, "Error snd_pcm_readi: [%s]", snd_strerror(r));
return -1;
}
if (r > 0) {
result += r;
count -= r;
data += r * record_handle->bits_per_frame / 8;
}
}
return result;
}
uint8_t *mozart_record_get_data(record_handle_t * record_handle)
{
if (read_pcm(record_handle) != record_handle->chunk_size)
return NULL;
return record_handle->data_buf;
}
void mozart_release_record_handle(record_handle_t * record_handle)
{
snd_pcm_drain(record_handle->handle);
free(record_handle->data_buf);
snd_output_close(record_handle->log);
snd_pcm_close(record_handle->handle);
}