本文主要是介绍alsa音频采集和播放,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
http://blog.csdn.net/liu_xiao_cheng/article/details/43017245
Alsa音频采集
#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 <assert.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <asm/byteorder.h>
#include <pthread.h>
#include "alsa/asoundlib.h"
#include "porting_mic.h"
#include "porting_debug.h"
/********************************************************************************************************
* Defines *
********************************************************************************************************/
/* do function and check return */
#define DOFUNC_CHECK(func) \
do{ \
ret = func; \
if ( ret < 0 ) \
{ \
HISI_ERR(PORT_MODULE_MIC, "(err)%s failed, ret = %s", #func, errno); \
goto LAB_ERR; \
} \
}while ( 0 )
#define SAMPLES 320 /*采样个数,320个就返回一次,太大了会等待太久*/
#define LOOP_BUF_LEN (1024*16)
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y);\
_x < _y ? _x : _y; })
/* 循环buffer */
typedef struct {
pthread_mutex_t mutex;
BYTE_T buffer[LOOP_BUF_LEN];
UINT32_T in;
UINT32_T out;
} loop_buf_t;
/* 保存音频数据到文件 */
//#define RECORD_DATA
/********************************************************************************************************
* Global Variables *
********************************************************************************************************/
/********************************************************************************************************
* Local Variables *
********************************************************************************************************/
/* used for DOFUNC_CHECK, save the function name in func_name */
static CHAR_T func_name[64];
static CHAR_T *pfunc;
static UINT32_T g_mic_handle = 0;
static pthread_t g_mic_thread = 0;
static UINT32_T g_thread_flag = 0;
static UINT32_T g_mic_status = 0; /* 0:stop capture; 1:start capture */
static UINT32_T g_puting_data = 0;
static loop_buf_t ring_buf;
static loop_buf_t *PMIC_buf = &ring_buf;
static UINT32_T g_bits_per_frame;
static UINT32_T g_bits_per_sample;
static BYTE_T *g_buf_getdata = NULL;
static BYTE_T *g_buf_transfchn = NULL;
static BYTE_T *g_buf_transfrate = NULL;
static INT32_T chn_mul = 1;/* 1:单声道; 2:双声道*/
static INT32_T rate_mul = 1; /* 基于8000Hz, 1:8000; 2:16000; 3:24000 ... */
snd_pcm_sframes_t (*readi_func)(snd_pcm_t *handle, void *buffer, snd_pcm_uframes_t size);
#ifdef RECORD_DATA
FILE *g_original_fp = NULL;
FILE *g_send_fp = NULL;
#endif
/********************************************************************************************************
* Local Functions Declaring *
********************************************************************************************************/
static INT32_T mic_init_loop_buf(loop_buf_t *buf);
static INT32_T mic_release_loop_buf(loop_buf_t *buf);
static INT32_T mic_put_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len);
static INT32_T mic_get_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len);
static VOID mic_lock_buf(loop_buf_t *buf);
static VOID mic_unlock_buf(loop_buf_t *buf);
static INT32_T mic_get_params(snd_pcm_format_t *format, UINT32_T *channels, UINT32_T *rate);
static INT32_T mic_set_params(UINT32_T handle);
static INT32_T mic_pcm_read(INT32_T ophandle, BYTE_T *data, UINT32_T rcount);
VOID *mic_usb_task(VOID);
/********************************************************************************************************
* Global Functions *
********************************************************************************************************/
INT32_T porting_mic_init(VOID)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
CHECK_MODULE_INIT_STATUS(PORT_MODULE_MIC);
SET_MODULE_INIT_STATUS(PORT_MODULE_MIC);
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
INT32_T porting_mic_exit(VOID)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
CHECK_MODULE_EXIT_STATUS(PORT_MODULE_MIC);
SET_MODULE_EXIT_STATUS(PORT_MODULE_MIC);
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
/*********************************************************************
功能说明:
打开一个声音采集设备实例。
参数说明:
输入参数:
index-指定 mic 的索引位置
输出参数:无
返 回:
!= IPANEL_NULL: 返回打开设备的句柄;
== IPANEL_NULL: 打开设备失败。
************************************************************************/
UINT32_T ipanel_porting_mic_open(IPANEL_MIC_NOTIFY func)
{
int ret;
HISI_INFO(PORT_MODULE_MIC, "(in)");
ret = mic_open();
if( ret == 0) goto LAB_ERR;
/* 注册音频数据回调函数 */
readi_func = snd_pcm_readi;
g_thread_flag = 1;
g_mic_thread = ipanel_porting_task_create("umic", mic_usb_task, NULL, 5, 0x8000);
if( g_mic_thread == 0 )
{
mic_close();
goto LAB_ERR;
}
#ifdef RECORD_DATA
/***************************************************************
g_original_fp保存麦克风拿到的原始数据,readi_func的data
g_send_fp保存传给中间件的数据,ipanel_porting_mic_read的data
***************************************************************/
g_original_fp = fopen("./save_from_capture_original_data.pcm", "w+");
if(!g_original_fp)
{
HISI_ERR(PORT_MODULE_MIC, "opne file save_from_capture_original_data failed!");
perror("open");
}
else
{
HISI_INFO(PORT_MODULE_MIC, "open save_from_capture_original_data file");
}
g_send_fp = fopen("./save_data_sending_ipanel.pcm", "w+");
if(!g_send_fp)
{
HISI_ERR(PORT_MODULE_MIC, "opne file g_send_fp failed!");
perror("open");
}
else
{
HISI_INFO(PORT_MODULE_MIC, "open save_from_capture_original_data file");
}
#endif
/************************************************************************
g_buf_getdata: 存放readi_func读到的原始数据
g_buf_transfchn: 原始数据数据转成单声道数据后存放到这个buf
g_buf_transfrate: 采样率转换成8K后的数据,该buf数据就是上传给中间件的数据
************************************************************************/
g_buf_getdata = malloc(SAMPLES * g_bits_per_frame / 8);
if (g_buf_getdata == NULL)
{
HISI_ERR(PORT_MODULE_MIC, "g_buf_getdata get memory failed!");
}
g_buf_transfchn = malloc(SAMPLES * g_bits_per_sample / 8);
if (g_buf_transfchn == NULL)
{
HISI_ERR(PORT_MODULE_MIC, "g_buf_transfchn get memory failed!");
}
g_buf_transfrate = malloc(SAMPLES);
if (g_buf_transfrate == NULL)
{
HISI_ERR(PORT_MODULE_MIC, "g_buf_transfrate get memory failed!");
}
HISI_INFO(PORT_MODULE_MIC,"(out)");
return g_mic_handle;
LAB_ERR:
return IPANEL_NULL;
}
/********************************************************************************************************
功能说明:
读取声音采样数据。
参数说明:
输入参数:
handle –声音采集设备实例句柄
buf - 指向数据块的指针
len - buf的长度
输出参数:
buf:读取的采样数据
返 回:
>=0: 函数执行成功,返回读取的数据长度;
IPANEL_ERR: 函数执行失败。
********************************************************************************************************/
INT32_T ipanel_porting_mic_read(UINT32_T handle, BYTE_T *buf, UINT32_T len)
{
INT32_T DataLen;
porting_verb(PORT_MODULE_MIC, "(in) expect len = %d", len);
if (g_puting_data)
{
porting_verb(PORT_MODULE_MIC, "(out) return 0");
return 0;
}
DataLen = 0;
if (g_mic_status)
{
mic_lock_buf(PMIC_buf);
DataLen = mic_get_loop_buf(PMIC_buf, buf, len);
mic_unlock_buf(PMIC_buf);
}
porting_verb(PORT_MODULE_MIC, "(out) read DataLen = %d", DataLen);
return DataLen;
}
/************************************************************************************
功能说明:
关闭通过 ipanel_porting_mic_open 打开的声音采集设备实例,同时要释放可能存
在的待处理数据块。
参数说明:
输入参数:
mic –声音采集设备实例句柄
输出参数:无
返 回:
IPANEL_OK: 函数执行成功;
IPANEL_ERR: 函数执行失败。
**********************************************************************************/
INT32_T ipanel_porting_mic_close(UINT32_T handle)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
g_thread_flag = 0;
ipanel_porting_task_destroy(g_mic_thread);
mic_close();
#ifdef RECORD_DATA
fclose(g_original_fp);
fclose(g_send_fp);
#endif
free(g_buf_getdata);
free(g_buf_transfchn);
free(g_buf_transfrate);
HISI_INFO(PORT_MODULE_MIC,"(out)");
return IPANEL_OK;
}
/************************************************************************************
功能说明:
对声音采集设备实例进行一个操作,或者用于设置和获取声音采集设备实例的参数和属性。
参数说明:
输入参数:
handle –声音采集设备实例句柄
op - 操作命令
typedef enum
{
IPANEL_MIC_START = 1,
IPANEL_MIC_STOP = 2,
IPANEL_MIC_CLEAR_BUFFER = 3,
IPANEL_MIC_SET_PARAM = 4,
} IPANEL_MIC_IOCTL_e;
arg – 操作命令所带的参数,当传递枚举型或32位整数值时,arg可强制转换成对应数据类型。
op和arg的取值关系见下表:
+-----------------------+-----------------------------------+--------------------+
|Op |Arg |说明 |
+-----------------------+-----------------------------------+--------------------+
|IPANEL_MIC_START |IPANEL_NULL |启动声音采集 |
+-----------------------+-----------------------------------+--------------------+
|IPANEL_MIC_STOP |IPANEL_NULL |停止声音采集 |
+-----------------------+-----------------------------------+--------------------+
|IPANEL_MIC_CLEAR_BUFFER|IPANEL_NULL |清空声音采集缓存 |
+-----------------------+-----------------------------------+--------------------+
|IPANEL_MIC_SET_PARAM |指向IPANEL_PCMDES 类型的一个指针, |设置采样参数 |
| |此时参数是个输入输出参数,如果设备 | |
| |没有用户指定的能力,则设备将自身能 | |
| |力在这个结构中返回 | |
+-----------------------+-----------------------------------+--------------------+
输出参数:无
返 回:
IPANEL_OK: 函数执行成功;
IPANEL_ERR: 函数执行失败。
**********************************************************************************/
INT32_T ipanel_porting_mic_ioctl(UINT32_T handle, IPANEL_MIC_IOCTL_e op, VOID *arg)
{
INT32_T i;
HISI_INFO(PORT_MODULE_MIC, "(in)");
if (handle != g_mic_handle)
{
HISI_ERR(PORT_MODULE_MIC, "(err) invalid handle!");
return IPANEL_ERR;
}
switch(op)
{
case IPANEL_MIC_START:
{
HISI_INFO(PORT_MODULE_MIC, "IPANEL_MIC_START");
if (1 == g_mic_status)
{
goto LABEL_OK;
}
g_mic_status = 1;
}
break;
case IPANEL_MIC_STOP:
{
HISI_INFO(PORT_MODULE_MIC, "IPANEL_MIC_STOP");
if(0 == g_mic_status)
{
goto LABEL_OK;
}
g_mic_status = 0;
usleep(200);
memset(PMIC_buf->buffer, 0, LOOP_BUF_LEN);
PMIC_buf->in = PMIC_buf->out = 0;
}
break;
case IPANEL_MIC_CLEAR_BUFFER:
{
HISI_INFO(PORT_MODULE_MIC, "IPANEL_MIC_CLEAR_BUFFER");
g_mic_status = 0;
usleep(200);
memset(PMIC_buf->buffer, 0, LOOP_BUF_LEN);
PMIC_buf->in = PMIC_buf->out = 0;
g_mic_status = 1;
}
break;
case IPANEL_MIC_SET_PARAM:
{
/* 中间件通过IPANEL_PCMDES设置音频格式,现在固定为PCM */
HISI_INFO(PORT_MODULE_MIC, "IPANEL_MIC_SET_PARAM TODO..");
}
break;
default:
{
HISI_ERR(PORT_MODULE_MIC, "(err)invalid op");
return IPANEL_ERR;
}
}
LABEL_OK:
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
/********************************************************************************************************
* Local Functions *
********************************************************************************************************/
int mic_open(void)
{
INT32_T ret;
snd_pcm_t *phandle;
snd_pcm_stream_t stream;
CHAR_T *pcm_name = "default";
HISI_INFO(PORT_MODULE_MIC, "(in)");
/* 录音设备固定为capture */
stream = SND_PCM_STREAM_CAPTURE;
if (!access("/dev/pcmC1D0c", F_OK))
{
HISI_INFO(PORT_MODULE_MIC, "access /dev/pcmC1D0c");
pcm_name = "hw:1";
}
else if (!access("/dev/pcmC0D0c", F_OK))
{
HISI_INFO(PORT_MODULE_MIC, "access /dev/pcmC0D0c");
pcm_name = "hw:0";
}
else
{
HISI_ERR(PORT_MODULE_MIC, "can not find capture device");
goto LAB_ERR;
}
DOFUNC_CHECK(snd_pcm_open(&phandle, pcm_name, stream, 0));
g_mic_handle = (UINT32_T)phandle;
HISI_INFO(PORT_MODULE_MIC, "snd_pcm_open, device name %s, handle %d", pcm_name, g_mic_handle);
mic_set_params(g_mic_handle);
mic_init_loop_buf(PMIC_buf);
HISI_INFO(PORT_MODULE_MIC,"(out)");
return phandle;
LAB_ERR:
return IPANEL_NULL;
}
int mic_close(void)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
snd_pcm_close((snd_pcm_t *)g_mic_handle);
g_mic_handle = 0;
mic_release_loop_buf(PMIC_buf);
HISI_INFO(PORT_MODULE_MIC,"(out)");
return IPANEL_OK;
}
static INT32_T mic_init_loop_buf(loop_buf_t *buf)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
memset(buf->buffer, 0, LOOP_BUF_LEN);
buf->in = 0;
buf->out = 0;
pthread_mutex_init(&buf->mutex, NULL);
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
static INT32_T mic_release_loop_buf(loop_buf_t *buf)
{
HISI_INFO(PORT_MODULE_MIC, "(in)");
pthread_mutex_destroy(&buf->mutex);
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
static INT32_T mic_put_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len)
{
UINT32_T num;
porting_verb(PORT_MODULE_MIC, "(in)");
len = min(len, LOOP_BUF_LEN - buf->in + buf->out);
/* first put the data starting from buf->in to buffer end */
num = min(len, LOOP_BUF_LEN - (buf->in & (LOOP_BUF_LEN - 1)));
memcpy(buf->buffer + (buf->in & (LOOP_BUF_LEN - 1)), buffer, num);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(buf->buffer, buffer + num, len - num);
buf->in += len;
if (buf->in == LOOP_BUF_LEN)
{
buf->in = 0;
}
porting_verb(PORT_MODULE_MIC, "(out) len = %d", len);
return len;
}
static INT32_T mic_get_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len)
{
UINT32_T num;
porting_verb(PORT_MODULE_MIC, "(in)");
len = min(len, buf->in - buf->out);
/* first get the data from buf->out until the end of the buffer */
num = min(len, LOOP_BUF_LEN - (buf->out & (LOOP_BUF_LEN - 1)));
memcpy(buffer, buf->buffer + (buf->out & (LOOP_BUF_LEN - 1)), num);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + num, buf->buffer, len - num);
buf->out += len;
if (buf->out == buf->in)
{
buf->out = 0;
buf->in = 0;
}
porting_verb(PORT_MODULE_MIC, "(out) len = %d", len);
return len;
}
VOID mic_lock_buf(loop_buf_t *buf)
{
pthread_mutex_lock(&buf->mutex);
}
VOID mic_unlock_buf(loop_buf_t *buf)
{
pthread_mutex_unlock(&buf->mutex);
}
static INT32_T mic_get_params(snd_pcm_format_t *format, UINT32_T *channels, UINT32_T *rate)
{
INT32_T i;
FILE * fp;
CHAR_T *buf;
CHAR_T *pt, *pt2;
CHAR_T param[128];
INT32_T size;
HISI_INFO(PORT_MODULE_MIC, "(in)");
/***************************************************************************************
拔掉或插入 USB MIC,单板上执行“ls /dev”命令,观察 pcm字母开头的 ALSA 设备列表变化情况。
以下是参考平台插入 USB MIC 后 ALSA设备列表:
pcmC0D0p //C0表示card0,D0表示device0,p表示playback
pcmC1D0c //C1表示card1,D0表示device0,c表示capture
硬件信息不能定死,Format、Channels、Rates要在设备信息文件中读取
# cat /proc/asound/card1/stream0
***************************************************************************************/
if (!access("/dev/pcmC1D0c", F_OK))
{
fp = fopen("/proc/asound/card1/stream0", "rt");
if (NULL == fp)
{
HISI_ERR(PORT_MODULE_MIC, "card1 file open failed");
goto LAB_ERR;
}
}
else if(!access("/dev/pcmC0D0c", F_OK))
{
fp = fopen("/proc/asound/card0/stream0", "rt");
if (NULL == fp)
{
HISI_ERR(PORT_MODULE_MIC, "card0 file open failed");
goto LAB_ERR;
}
}
else
{
HISI_ERR(PORT_MODULE_MIC, "can not find capture device");
goto LAB_ERR;
}
buf = (CHAR_T *)malloc(2048 * sizeof(CHAR_T));
if (NULL == buf)
{
HISI_ERR(PORT_MODULE_MIC, "buf malloc failed");
fclose(fp);
goto LAB_ERR;
}
size = fread(buf, sizeof(CHAR_T), 2048, fp);
if (size <= 0)
{
HISI_ERR(PORT_MODULE_MIC, "file read failed");
free(buf);
buf = NULL;
fclose(fp);
goto LAB_ERR;
}
/* 检索format */
if((pt = strstr(buf, "S16_LE")) != 0 )
{
*format = SND_PCM_FORMAT_S16_LE;
HISI_INFO(PORT_MODULE_MIC, "format is SND_PCM_FORMAT_S16_LE");
}
else
{
HISI_ERR(PORT_MODULE_MIC, "(err)unkonw format");
goto LAB_ERR;
}
pt2 = buf;
/* 检索channels */
while((pt = strstr(pt2, "Channels:")) != 0)
{
i = 0;
while (*(pt+i) != '\n') i++;
strncpy(param, pt, i);
if (strchr(param, '1'))
{
*channels = 1;
chn_mul = 1;
break;
}
else if (strchr(param, '2'))
{
*channels = 2;
chn_mul = 2;
break;
}
else
{
HISI_ERR(PORT_MODULE_MIC, "(err)unkonw channels");
goto LAB_ERR;
}
pt2 = pt+i;
}
HISI_INFO(PORT_MODULE_MIC, "channels is %d", *channels);
pt2 = buf;
/* 检索rate */
while((pt = strstr(pt2, "Rates:")) != 0)
{
i = 0;
while (*(pt+i) != '\n') i++;
strncpy(param, pt, i);
if (strstr(param, " 8000") != 0)
{
HISI_INFO(PORT_MODULE_MIC, "Rates:8000");
*rate = 8000;
rate_mul = 1;
break;
}
else if (strstr(param, "16000") != 0)
{
HISI_INFO(PORT_MODULE_MIC, "Rates:16000");
*rate = 16000;
rate_mul = 2;
break;
}
pt2 = pt+i;
}
if(!pt)
{
porting_warn(PORT_MODULE_MIC, "unkonw rate, set default 8000");
*rate = 8000;
rate_mul = 1;
}
free(buf);
buf = IPANEL_NULL;
fclose(fp);
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
LAB_ERR:
HISI_ERR(PORT_MODULE_MIC, "(err)");
return IPANEL_ERR;
}
static INT32_T mic_set_params(UINT32_T handle)
{
INT32_T ret;
UINT32_T n;
snd_pcm_format_t format;
UINT32_T channels;
UINT32_T rate;
snd_pcm_t *phandle;
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *swparams;
UINT32_T buffer_time = 0;
UINT32_T period_time = 0;
snd_pcm_uframes_t buffer_frames = 0;
snd_pcm_uframes_t chunk_size = 0;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t start_threshold, stop_threshold;
HISI_INFO(PORT_MODULE_MIC, "(in)");
phandle = (snd_pcm_t *)handle;
DOFUNC_CHECK(mic_get_params(&format, &channels, &rate));
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_sw_params_alloca(&swparams);
DOFUNC_CHECK(snd_pcm_hw_params_any(phandle, params));
DOFUNC_CHECK(snd_pcm_hw_params_set_access(phandle, params, SND_PCM_ACCESS_RW_INTERLEAVED));
DOFUNC_CHECK(snd_pcm_hw_params_set_format(phandle, params, format));
DOFUNC_CHECK(snd_pcm_hw_params_set_channels(phandle, params, channels));
DOFUNC_CHECK(snd_pcm_hw_params_set_rate_near(phandle, params, &rate, 0));
DOFUNC_CHECK(snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0));
if (buffer_time > 500000)
{
buffer_time = 500000;
}
period_time = buffer_time / 4;
DOFUNC_CHECK(snd_pcm_hw_params_set_period_time_near(phandle, params, &period_time, 0));
if (buffer_time > 0)
{
DOFUNC_CHECK(snd_pcm_hw_params_set_buffer_time_near(phandle, params, &buffer_time, 0));
}
else
{
DOFUNC_CHECK(snd_pcm_hw_params_set_buffer_size_near(phandle, params, &buffer_frames));
}
DOFUNC_CHECK(snd_pcm_hw_params(phandle, params));
chunk_size = 1024;
DOFUNC_CHECK(snd_pcm_hw_params_get_period_size(params, &chunk_size, 0));
DOFUNC_CHECK(snd_pcm_hw_params_get_buffer_size(params, &buffer_size));
if (chunk_size == buffer_size)
{
HISI_ERR(PORT_MODULE_MIC, "Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size);
goto LAB_ERR;
}
DOFUNC_CHECK(snd_pcm_sw_params_current(phandle, swparams));
n = chunk_size;
DOFUNC_CHECK(snd_pcm_sw_params_set_avail_min(phandle, swparams, n));
n = buffer_size;
start_threshold = (double)rate / 1000000;
if (start_threshold < 1)
{
start_threshold = 1;
}
if (start_threshold > n)
{
start_threshold = n;
}
DOFUNC_CHECK(snd_pcm_sw_params_set_start_threshold(phandle, swparams, start_threshold));
stop_threshold = buffer_size;
DOFUNC_CHECK(snd_pcm_sw_params_set_stop_threshold(phandle, swparams, stop_threshold));
DOFUNC_CHECK(snd_pcm_sw_params(phandle, swparams));
g_bits_per_sample = snd_pcm_format_physical_width(format);
g_bits_per_frame = g_bits_per_sample * channels;
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
LAB_ERR:
HISI_ERR(PORT_MODULE_MIC, "(err)");
return IPANEL_ERR;
}
static INT32_T mic_pcm_read(INT32_T ophandle, BYTE_T *data, UINT32_T rcount)
{
INT32_T r;
UINT32_T count;
snd_pcm_t *readhandle;
porting_verb(PORT_MODULE_MIC, "(in)");
readhandle = (snd_pcm_t*)ophandle;
if(readhandle == 0) return IPANEL_ERR;
count = rcount;
while (count > 0)
{
r = readi_func(readhandle, data, count);
if (r < 0)
{
usleep(1000);
return IPANEL_ERR;
}
else
{
count -= r;
data += r * g_bits_per_frame / 8;
}
}
porting_verb(PORT_MODULE_MIC, "(out)");
return rcount;
}
VOID *mic_usb_task(VOID)
{
INT32_T i, ret = 0;
INT32_T readlen;
INT32_T tmp_data;
BYTE_T *buf_pre;
BYTE_T *buf_send;
/* 原始数据装换声道数所需的变量 */
INT16_T *buf_chn_r;
INT16_T *buf_chn_w;
INT32_T chn_left;
INT32_T chn_right;
HISI_INFO(PORT_MODULE_MIC, "(in)");
while (g_thread_flag)
{
if (!g_mic_status)
{
usleep(1000);
continue;
}
readlen = mic_pcm_read(g_mic_handle, g_buf_getdata, SAMPLES);
if (readlen <= 0)
{
usleep(10000);
if (access("/dev/pcmC1D0c", F_OK)!=0 && access("/dev/pcmC0D0c", F_OK)!=0)
{
HISI_INFO(PORT_MODULE_MIC, "(err)can not find MIC decive!");
if (g_mic_handle != 0 )
mic_close();
}
else
{
HISI_INFO(PORT_MODULE_MIC, "(err)not read the data of MIC!");
if (g_mic_handle == 0 )
{
sleep(1); //重新插上后不要马上打开
mic_open();
}
}
continue;
}
#ifdef RECORD_DATA
fwrite((void *)g_buf_getdata, 1, readlen * g_bits_per_frame / 8, g_original_fp);
#endif
/**************************************************************************
语音处理而言8K采样率便足够,若高于8K,丢弃数据使之转换成8K采样率
g_bits_per_frame = g_bits_per_sample * channels
单声道一帧的大小:16 * 1 / 8 = 2字节
双声道一帧的大小:16 * 2 / 8 = 4字节
中间件只处理单声道数据,一帧上双声道数据转换为单声道数据处理方法:
左声道数据加上右声道数据除以2
采样率转换方法:抽样保留,即16K则每2帧传一帧上去,32K则每4帧传一帧上去
readlen表示一次读到的帧数,先转换成单声道帧,再转换采样率
g_buf_getdata: 存放readi_func读到的原始数据
g_buf_transfchn: 原始数据数据转成单声道数据后存放到这个buf
g_buf_transfrate: 采样率转换成8K后的数据,该buf数据就是上传给中间件的数据
**************************************************************************/
if (1 == chn_mul)
{
buf_pre = g_buf_getdata;
}
else if (2 == chn_mul)
{
buf_chn_r = (INT16_T *)g_buf_getdata;
buf_chn_w = (INT16_T *)g_buf_transfchn;
for (i = 0; i <= readlen * g_bits_per_frame / 8; i += 4)
{
chn_left = *buf_chn_r++;
chn_right = *buf_chn_r++;
*buf_chn_w++ = (INT16_T)((chn_left + chn_right) >> 1);
}
buf_pre = g_buf_transfchn;
}
if (1 == rate_mul)
{
buf_send = buf_pre;
}
else
{
for (i = 0; i <= readlen * g_bits_per_sample / 8; i += (2 * rate_mul))
{
memcpy(g_buf_transfrate + i / 2, buf_pre + i, 2);
}
buf_send = g_buf_transfrate;
}
g_puting_data = 1;
mic_lock_buf(PMIC_buf);
mic_put_loop_buf(PMIC_buf, buf_send, readlen * g_bits_per_frame / 8 /chn_mul/rate_mul);
mic_unlock_buf(PMIC_buf);
g_puting_data = 0;
usleep(100);
}
HISI_INFO(PORT_MODULE_MIC, "(out)");
return IPANEL_OK;
}
/* EOF */
2.Alsa音频播放
#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 <assert.h>
#include <sys/poll.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <asm/byteorder.h>
#include <pthread.h>
#include "alsa/asoundlib.h"
#include "ipanel_mixer.h"
#include "ipanel_debug.h"
#include "hisi_util.h"
#if 1
/********************************************************************************************************
* Defines *
********************************************************************************************************/
/* do function and check return */
#define DOFUNC_CHECK(func) \
do{ \
ret = func; \
if ( ret < 0 ) \
{ \
HISI_ERR(MODULE_PRINT_CAM, "(err)%s failed, ret = %s", #func, errno); \
goto LAB_ERR; \
} \
}while ( 0 )
#define LOOP_BUF_LEN (1024*16)
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y);\
_x < _y ? _x : _y; })
/* 循??buffer */
typedef struct {
pthread_mutex_t mutex;
BYTE_T buffer[LOOP_BUF_LEN];
UINT32_T in;
UINT32_T out;
} loop_buf_t;
/********************************************************************************************************
* Global Variables *
********************************************************************************************************/
/********************************************************************************************************
* Local Variables *
********************************************************************************************************/
/* used for DOFUNC_CHECK, save the function name in func_name */
static CHAR_T func_name[64];
static CHAR_T *pfunc;
static snd_pcm_t *g_mixer_handle;
static pthread_t g_mixer_thread = 0;
static UINT32_T g_thread_flag = 0;
static loop_buf_t ring_buf;
static loop_buf_t *PMIXER_buf = &ring_buf;
static UINT32_T g_bits_per_frame;
static UINT32_T g_bits_per_sample;
static INT32_T chn_mul = 1;/* 1:??????; 2:双????*/
static INT32_T rate_mul = 1; /* ????8000Hz, 1:8000; 2:16000; 3:24000 ... */
static snd_pcm_uframes_t chunk_size = 0;
/********************************************************************************************************
* Local Functions Declaring *
********************************************************************************************************/
static INT32_T mixer_init_loop_buf(loop_buf_t *buf);
static INT32_T mixer_release_loop_buf(loop_buf_t *buf);
static INT32_T mixer_put_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len);
static INT32_T mixer_get_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len);
static VOID mixer_lock_buf(loop_buf_t *buf);
static VOID mixer_unlock_buf(loop_buf_t *buf);
static INT32_T mixer_get_params(snd_pcm_format_t *format, UINT32_T *channels, UINT32_T *rate);
static INT32_T mixer_set_params(UINT32_T handle);
static IPANEL_AUDIO_MIXER_NOTIFY callBackFunc;
static VOID mixer_usb_task(VOID);
static UINT32_T mixer_open(void);
static INT32_T mixer_set_volume(long vol);
/*********************************************************************
????说????
????一??PCM????实??
????说???? ????一??PCM ?????璞甘????为?私?????源??突???猓?
??约??(?俣?只????一??硬???璞??????,默??为0);
???????汛???实?????远?占??式??IPANEL_DEV_USE_EXCUSIVE??????时??
?????????写蚩??鞫疾??晒?????前??实???????怨??矸绞?
??IPANEL_DEV_USE_SHARED??????时???????俅蚩???实???投?占实????
???????卸??枪???实???????????瘸???式???牛?
??????实???嵌?占??式?蚩?????前???泄???实???????鼋?????????????
??????????mode?? 使???璞?姆?式;????Func: ?????氐?指??.
??mixer_send????xmemblk??使?煤?,?氐?通知?屑???,?洗未?????buf?????头?,
????写 ,?氐???event值为IPANEL_AUDIO_DATA_CONSUMED,????实??.
????????????
?? ?兀?
???? IPANEL_NULL: ???卮????璞?木???;
???? IPANEL_NULL: ?????璞甘?堋?
************************************************************************/
UINT32_T ipanel_porting_audio_mixer_open(IPANEL_DEV_USE_MODE mode,IPANEL_AUDIO_MIXER_NOTIFY func)
{
UINT32_T ret;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
if (NULL == func)
{
HISI_ERR(MODULE_PRINT_CAM, "Call back function is NULL!");
goto LAB_ERR;
}
callBackFunc = func;
ret = mixer_open();
if(0 == ret) goto LAB_ERR;
g_thread_flag = 1;
g_mixer_thread = ipanel_porting_task_create("mixer", mixer_usb_task, NULL, 5, 0x8000);
if( g_mixer_thread == 0 )
{
mixer_close();
goto LAB_ERR;
}
HISI_INFO(MODULE_PRINT_CAM,"(out)");
return g_mixer_handle;
LAB_ERR:
return IPANEL_NULL;
}
/********************************************************************************************************
????说????
??????sound????PCM ???荨?buf ??????实?实????荨?
????说????
??????????
handle ?Cmixer?璞????
pcmblk-- PCM ??????????????息??
????????????
???????兀?
????IPANEL_OK:?晒?;
IPANEL_ERR: ????执??失?堋?
********************************************************************************************************/
INT32_T ipanel_porting_audio_mixer_memblk_send(UINT32_T handle, IPANEL_XMEMBLK *pcmblk)
{
INT32_T date_len;
mixer_lock_buf(PMIXER_buf);
date_len = mixer_put_loop_buf(PMIXER_buf, pcmblk->pbuf, pcmblk->len);
printf("Send pcm data to the loop buffer len = %d,pbuf->in=%d,pbuf->out=%d!\n",date_len,PMIXER_buf->in,PMIXER_buf->out);
mixer_unlock_buf(PMIXER_buf);
usleep(20000);
callBackFunc(g_mixer_handle,IPANEL_AUDIO_DATA_CONSUMED,(UINT32_T *)pcmblk);
return date_len;
}
/************************************************************************************
????说????
?乇?通?? ipanel_porting_mixer_open ?蚩??????杉??璞甘????同时要?头趴??艽?
?诘拇????????菘椤?
????说????
??????????
mixer ?C?????杉??璞甘??????
????????????
?? ?兀?
IPANEL_OK: ????执?谐晒?;
IPANEL_ERR: ????执??失?堋?
**********************************************************************************/
INT32_T ipanel_porting_audio_mixer_close(UINT32_T handle)
{
HISI_INFO(MODULE_PRINT_CAM, "(in)");
g_thread_flag = 0;
ipanel_porting_task_destroy(g_mixer_thread);
mixer_close();
HISI_INFO(MODULE_PRINT_CAM,"(out)");
return IPANEL_OK;
}
/************************************************************************************
????说????
??????sound?璞????一??????,???????????煤突?取mixer ?璞甘???????牟????????浴?
????说????
??????????
handle ?C?????杉??璞甘??????
op ?? ????????
?ttypedef enum
????{
???? IPANEL_MIXER_SET_VOLUME = 1,
???? IPANEL_MIXER_CLEAR_BUFFER = 2,
???? .....
????} IPANEL_MIXER_IOCTL_e;
????arg ?C ?????????????牟??贝???枚???突?32 位????值时??
arg ??强??转???啥?应???????汀?????只???芯???VOIP????实??使?玫??募???枚??值.
????op ??arg ??取值??系???卤恚?
OP ARG 说??
IPANEL_MIXER_CLEAR_BUFFER IPANEL_NULL ???栈????????指????0
IPANEL_MIXER_SET_VOLUME ??????量 ?煞趴?
????????????
?? ?兀?
IPANEL_OK: ????执?谐晒?;
IPANEL_ERR: ????执??失?堋?
**********************************************************************************/
INT32_T ipanel_porting_audio_mixer_ioctl(UINT32_T handle, IPANEL_MIXER_IOCTL_e op, VOID *arg)
{
INT32_T i;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
if (handle != g_mixer_handle)
{
HISI_ERR(MODULE_PRINT_CAM, "(err) invalid handle!");
return IPANEL_ERR;
}
switch(op)
{
case IPANEL_MIXER_CLEAR_BUFFER:
{
HISI_INFO(MODULE_PRINT_CAM, "IPANEL_MIXER_CLEAR_BUFFER");
mixer_lock_buf(PMIXER_buf);
memset(PMIXER_buf->buffer, 0, LOOP_BUF_LEN);
PMIXER_buf->in = PMIXER_buf->out = 0;
mixer_unlock_buf(PMIXER_buf);
}
break;
case IPANEL_MIXER_SET_VOLUME:
{
mixer_set_volume(*(long *)arg);
HISI_INFO(MODULE_PRINT_CAM, "IPANEL_MIXER_SET_VOLUME");
}
break;
default:
{
HISI_ERR(MODULE_PRINT_CAM, "(err)invalid op");
return IPANEL_ERR;
}
}
LABEL_OK:
HISI_INFO(MODULE_PRINT_CAM, "(out)");
return IPANEL_OK;
}
/********************************************************************************************************
* Local Functions *
********************************************************************************************************/
static UINT32_T mixer_open(void)
{
INT32_T ret;
snd_pcm_t *phandle;
snd_pcm_stream_t stream;
CHAR_T *pcm_name = "default";
HISI_INFO(MODULE_PRINT_CAM, "(in)");
/* PCM ?????璞?潭?为playback */
stream = SND_PCM_STREAM_PLAYBACK;
if (!access("/dev/pcmC1D0p", F_OK))
{
HISI_INFO(MODULE_PRINT_CAM, "access /dev/pcmC1D0p");
pcm_name = "hw:1";
}
else if (!access("/dev/pcmC0D0p", F_OK))
{
HISI_INFO(MODULE_PRINT_CAM, "access /dev/pcmC0D0p");
pcm_name = "hw:0";
}
else
{
HISI_ERR(MODULE_PRINT_CAM, "can not find capture device");
goto LAB_ERR;
}
DOFUNC_CHECK(snd_pcm_open(&phandle, pcm_name, stream, 0));
g_mixer_handle = (UINT32_T)phandle;
HISI_INFO(MODULE_PRINT_CAM, "snd_pcm_open, device name %s, handle %d", pcm_name, g_mixer_handle);
mixer_set_params(g_mixer_handle);
mixer_init_loop_buf(PMIXER_buf);
HISI_INFO(MODULE_PRINT_CAM,"(out)");
return phandle;
LAB_ERR:
return 0;
}
int mixer_close(void)
{
HISI_INFO(MODULE_PRINT_CAM, "(in)");
snd_pcm_close((snd_pcm_t *)g_mixer_handle);
g_mixer_handle = 0;
mixer_release_loop_buf(PMIXER_buf);
HISI_INFO(MODULE_PRINT_CAM,"(out)");
return IPANEL_OK;
}
static INT32_T mixer_init_loop_buf(loop_buf_t *buf)
{
HISI_INFO(MODULE_PRINT_CAM, "(in)");
memset(buf->buffer, 0, LOOP_BUF_LEN);
buf->in = 0;
buf->out = 0;
pthread_mutex_init(&buf->mutex, NULL);
HISI_INFO(MODULE_PRINT_CAM, "(out)");
return IPANEL_OK;
}
static INT32_T mixer_release_loop_buf(loop_buf_t *buf)
{
HISI_INFO(MODULE_PRINT_CAM, "(in)");
pthread_mutex_destroy(&buf->mutex);
HISI_INFO(MODULE_PRINT_CAM, "(out)");
return IPANEL_OK;
}
static INT32_T mixer_put_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len)
{
UINT32_T num;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
len = min(len, LOOP_BUF_LEN - buf->in + buf->out);
/* first put the data starting from buf->in to buffer end */
num = min(len, LOOP_BUF_LEN - (buf->in & (LOOP_BUF_LEN - 1)));
memcpy(buf->buffer + (buf->in & (LOOP_BUF_LEN - 1)), buffer, num);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(buf->buffer, buffer + num, len - num);
buf->in += len;
if (buf->in == LOOP_BUF_LEN)
{
buf->in = 0;
}
HISI_INFO(MODULE_PRINT_CAM, "(out) len = %d", len);
return len;
}
static INT32_T mixer_get_loop_buf(loop_buf_t *buf, BYTE_T *buffer, UINT32_T len)
{
UINT32_T num;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
len = min(len, buf->in - buf->out);
/* first get the data from buf->out until the end of the buffer */
num = min(len, LOOP_BUF_LEN - (buf->out & (LOOP_BUF_LEN - 1)));
memcpy(buffer, buf->buffer + (buf->out & (LOOP_BUF_LEN - 1)), num);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + num, buf->buffer, len - num);
buf->out += len;
if (buf->out == buf->in)
{
buf->out = 0;
buf->in = 0;
}
HISI_INFO(MODULE_PRINT_CAM, "(out) len = %d", len);
return len;
}
VOID mixer_lock_buf(loop_buf_t *buf)
{
pthread_mutex_lock(&buf->mutex);
}
VOID mixer_unlock_buf(loop_buf_t *buf)
{
pthread_mutex_unlock(&buf->mutex);
}
static INT32_T mixer_get_params(snd_pcm_format_t *format, UINT32_T *channels, UINT32_T *rate)
{
INT32_T i;
FILE * fp;
CHAR_T *buf;
CHAR_T *pt, *pt2;
CHAR_T param[128];
INT32_T size;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
/***************************************************************************************
?蔚??????? USB MIC????????执?小?ls /dev?????睿?鄄? pcm??母??头?? ALSA ?璞?斜??浠??????
?????遣慰?平台???? USB MIC ?? ALSA?璞?斜恚?
pcmC0D0p //C0??示card0??D0??示device0??p??示playback
pcmC1D0c //C1??示card1??D0??示device0??c??示capture
硬????息???芏??溃?Format??Channels??Rates要???璞??息?募??卸?取
# cat /proc/asound/card1/stream0
***************************************************************************************/
if (!access("/dev/pcmC0D0p", F_OK))
{
fp = fopen("/proc/asound/card0/stream0", "rt");
if (NULL == fp)
{
HISI_ERR(MODULE_PRINT_CAM, "card0 file open failed");
goto LAB_ERR;
}
}
else if(!access("/dev/pcmC1D0p", F_OK))
{
fp = fopen("/proc/asound/card1/stream0", "rt");
if (NULL == fp)
{
HISI_ERR(MODULE_PRINT_CAM, "card1 file open failed");
goto LAB_ERR;
}
}
else
{
HISI_ERR(MODULE_PRINT_CAM, "can not find capture device");
goto LAB_ERR;
}
buf = (CHAR_T *)malloc(2048 * sizeof(CHAR_T));
if (NULL == buf)
{
HISI_ERR(MODULE_PRINT_CAM, "buf malloc failed");
fclose(fp);
goto LAB_ERR;
}
size = fread(buf, sizeof(CHAR_T), 2048, fp);
if (size <= 0)
{
HISI_ERR(MODULE_PRINT_CAM, "file read failed");
free(buf);
buf = NULL;
fclose(fp);
goto LAB_ERR;
}
/* ????format */
if((pt = strstr(buf, "S16_LE")) != 0 )
{
*format = SND_PCM_FORMAT_S16_LE;
HISI_INFO(MODULE_PRINT_CAM, "format is SND_PCM_FORMAT_S16_LE");
}
else
{
HISI_ERR(MODULE_PRINT_CAM, "(err)unkonw format");
goto LAB_ERR;
}
pt2 = buf;
/* ????channels */
while((pt = strstr(pt2, "Channels:")) != 0)
{
i = 0;
while (*(pt+i) != '\n') i++;
strncpy(param, pt, i);
if (strchr(param, '1'))
{
*channels = 1;
chn_mul = 1;
break;
}
else if (strchr(param, '2'))
{
*channels = 2;
chn_mul = 2;
break;
}
else
{
HISI_ERR(MODULE_PRINT_CAM, "(err)unkonw channels");
goto LAB_ERR;
}
pt2 = pt+i;
}
HISI_INFO(MODULE_PRINT_CAM, "channels is %d", *channels);
pt2 = buf;
/* ????rate */
while((pt = strstr(pt2, "Rates:")) != 0)
{
i = 0;
while (*(pt+i) != '\n') i++;
strncpy(param, pt, i);
if (strstr(param, " 8000") != 0)
{
HISI_INFO(MODULE_PRINT_CAM, "Rates:8000");
*rate = 8000;
rate_mul = 1;
break;
}
else if (strstr(param, "16000") != 0)
{
HISI_INFO(MODULE_PRINT_CAM, "Rates:16000");
*rate = 16000;
rate_mul = 2;
break;
}
pt2 = pt+i;
}
if(!pt)
{
HISI_INFO(MODULE_PRINT_CAM, "unkonw rate, set default 8000");
*rate = 8000;
rate_mul = 1;
}
free(buf);
buf = IPANEL_NULL;
fclose(fp);
HISI_INFO(MODULE_PRINT_CAM, "(out)");
return IPANEL_OK;
LAB_ERR:
HISI_ERR(MODULE_PRINT_CAM, "(err)");
return IPANEL_ERR;
}
static INT32_T mixer_set_params(UINT32_T handle)
{
INT32_T ret;
UINT32_T n;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
UINT32_T channels = 2;
UINT32_T rate = 8000;
snd_pcm_t *phandle;
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *swparams;
UINT32_T buffer_time = 0;
UINT32_T period_time = 0;
snd_pcm_uframes_t buffer_frames = 0;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t start_threshold, stop_threshold;
HISI_INFO(MODULE_PRINT_CAM, "(in)");
phandle = (snd_pcm_t *)handle;
//DOFUNC_CHECK(mixer_get_params(&format, &channels, &rate));
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_sw_params_alloca(&swparams);
DOFUNC_CHECK(snd_pcm_hw_params_any(phandle, params));
DOFUNC_CHECK(snd_pcm_hw_params_set_access(phandle, params, SND_PCM_ACCESS_RW_INTERLEAVED));
DOFUNC_CHECK(snd_pcm_hw_params_set_format(phandle, params, format));
DOFUNC_CHECK(snd_pcm_hw_params_set_channels(phandle, params, channels));
DOFUNC_CHECK(snd_pcm_hw_params_set_rate_near(phandle, params, &rate, 0));
DOFUNC_CHECK(snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0));
if (buffer_time > 500000)
{
buffer_time = 500000;
}
period_time = buffer_time / 4;
DOFUNC_CHECK(snd_pcm_hw_params_set_period_time_near(phandle, params, &period_time, 0));
if (buffer_time > 0)
{
DOFUNC_CHECK(snd_pcm_hw_params_set_buffer_time_near(phandle, params, &buffer_time, 0));
}
else
{
DOFUNC_CHECK(snd_pcm_hw_params_set_buffer_size_near(phandle, params, &buffer_frames));
}
DOFUNC_CHECK(snd_pcm_hw_params(phandle, params));
chunk_size = 1024;
DOFUNC_CHECK(snd_pcm_hw_params_get_period_size(params, &chunk_size, 0));
DOFUNC_CHECK(snd_pcm_hw_params_get_buffer_size(params, &buffer_size));
if (chunk_size == buffer_size)
{
HISI_ERR(MODULE_PRINT_CAM, "Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size);
goto LAB_ERR;
}
DOFUNC_CHECK(snd_pcm_sw_params_current(phandle, swparams));
n = chunk_size;
DOFUNC_CHECK(snd_pcm_sw_params_set_avail_min(phandle, swparams, n));
n = buffer_size;
start_threshold = (double)rate / 1000000;
if (start_threshold < 1)
{
start_threshold = 1;
}
if (start_threshold > n)
{
start_threshold = n;
}
DOFUNC_CHECK(snd_pcm_sw_params_set_start_threshold(phandle, swparams, start_threshold));
stop_threshold = buffer_size;
//DOFUNC_CHECK(snd_pcm_sw_params_set_stop_threshold(phandle, swparams, stop_threshold));
DOFUNC_CHECK(snd_pcm_sw_params(phandle, swparams));
DOFUNC_CHECK(snd_pcm_prepare(phandle));
g_bits_per_sample = snd_pcm_format_physical_width(format);
g_bits_per_frame = g_bits_per_sample * channels;
HISI_INFO(MODULE_PRINT_CAM, "(out)");
return IPANEL_OK;
LAB_ERR:
HISI_ERR(MODULE_PRINT_CAM, "(err)");
return IPANEL_ERR;
}
static VOID mixer_usb_task(VOID)
{
INT32_T ret;
INT32_T len;
snd_pcm_uframes_t frames_to_deliver;
snd_pcm_uframes_t frameCount;
BYTE_T buffer[LOOP_BUF_LEN] = {0};
while (g_thread_flag)
{
printf("Start to write pcm data to the device!\n");
if ((ret = snd_pcm_wait(g_mixer_handle,1000)) < 0)
{
snd_pcm_prepare(g_mixer_handle);
HISI_ERR(MODULE_PRINT_CAM, "Device is not prepared!");
continue;
}
if ((frames_to_deliver = snd_pcm_avail_update(g_mixer_handle)) < 0)
{
if (frames_to_deliver == -EPIPE)
{
HISI_ERR(MODULE_PRINT_CAM, "An xrun occurred!");
}
else if (frames_to_deliver < 0)
{
HISI_ERR(MODULE_PRINT_CAM, "Unknown ALSA avail update return value!");
}
return;
}
frames_to_deliver = frames_to_deliver > chunk_size ? chunk_size : frames_to_deliver;
printf("frames_to_deliver is %d,chunk_size is %d\n",frames_to_deliver,chunk_size);
len = frames_to_deliver*g_bits_per_frame/8;
len = len > LOOP_BUF_LEN ? LOOP_BUF_LEN : len;
printf("Length is %d\n",len);
mixer_lock_buf(PMIXER_buf);
ret = mixer_get_loop_buf(PMIXER_buf, buffer, 320);
if (0 == ret)
{
mixer_unlock_buf(PMIXER_buf);
printf("Read length is 0!\n");
usleep(20000);
continue;
}
mixer_unlock_buf(PMIXER_buf);
frameCount = ret*8/g_bits_per_frame;
if (frameCount)
{
printf("Write pcm data to the device!\n");
if ((ret = snd_pcm_writei(g_mixer_handle, buffer, frameCount))<0)
{
usleep(2000);
if (ret == -EPIPE)
{
/* EPIPE means underrun */
HISI_ERR(MODULE_PRINT_CAM, "underrun occurred!");
}
else if (ret < 0)
{
HISI_ERR(MODULE_PRINT_CAM, "Send PCM date error!");
}
}
}
else
{
HISI_INFO(MODULE_PRINT_CAM, "There is no enough date in the loop buffer!");
usleep(10000);
}
}
}
static INT32_T mixer_set_volume(long vol)
{
snd_mixer_t *mixerFd;
snd_mixer_elem_t *elem;
INT32_T result;
INT32_T minVolume = 0;
INT32_T maxVolume = 0;
/*?蚩?????*/
if ((result = snd_mixer_open( &mixerFd, 0)) < 0)
{
mixerFd = NULL;
HISI_ERR(MODULE_PRINT_CAM, "Mixer open error!");
}
if ((result = snd_mixer_attach( mixerFd, "hw:0")) < 0)
{
snd_mixer_close(mixerFd);
mixerFd = NULL;
HISI_ERR(MODULE_PRINT_CAM, "Mixer attach error!");
}
if ((result = snd_mixer_selem_register( mixerFd, NULL, NULL)) < 0)
{
snd_mixer_close(mixerFd);
mixerFd = NULL;
HISI_ERR(MODULE_PRINT_CAM, "Mixer register error!");
}
if ((result = snd_mixer_load( mixerFd)) < 0)
{
snd_mixer_close(mixerFd);
mixerFd = NULL;
HISI_ERR(MODULE_PRINT_CAM, "Mixer load error!");
}
for(elem=snd_mixer_first_elem(mixerFd); elem; elem=snd_mixer_elem_next(elem))
{
if (snd_mixer_elem_get_type(elem) == SND_MIXER_ELEM_SIMPLE && snd_mixer_selem_is_active(elem))
{
if(strcmp(snd_mixer_selem_get_name(elem),"Master") == 0)
{
if(snd_mixer_selem_has_playback_volume(elem) != 0)
{
snd_mixer_selem_get_playback_volume_range(elem, &minVolume, &maxVolume);
snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
snd_mixer_selem_set_playback_volume_all(elem, vol);
}
}
}
}
return 0;
}
#endif
3.Linphone中的音频采集和播放部分
#include <alsa/asoundlib.h>
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/mssndcard.h"
#if 1//add by jzby 专门起一个线程来处理PCM数据
#define THREADED_VERSION
#else
//#define THREADED_VERSION
#endif
//add by jzby
#define PIPE_AUDIO_OUTPUT /* lxcheng add 2013-11-28 */
/*in case of troubles with a particular driver, try incrementing ALSA_PERIOD_SIZE
to 512, 1024, 2048, 4096...
then try incrementing the number of periods*/
#define ALSA_PERIODS 8
//#define ALSA_PERIOD_SIZE 256
#define ALSA_PERIOD_SIZE 1024
/*uncomment the following line if you have problems with an alsa driver
having sound quality trouble:*/
/*#define EPIPE_BUGFIX 1*/
static MSSndCard * alsa_card_new(int id);
static MSSndCard *alsa_card_duplicate(MSSndCard *obj);
static MSFilter * ms_alsa_read_new(const char *dev);
static MSFilter * ms_alsa_write_new(const char *dev);
struct _AlsaData{
char *pcmdev;
char *mixdev;
};
typedef struct _AlsaData AlsaData;
//#define RECORDFILE
#ifdef RECORDFILE
static int tempFd;
static FILE *rfilefd = NULL;//fopen("/media/alsaRead.pcm","w+");
#endif
static void alsa_resume(snd_pcm_t *handle){
int err;
snd_pcm_status_t *status=NULL;
snd_pcm_status_alloca(&status);
if ((err=snd_pcm_status(handle,status))!=0){
ms_warning("snd_pcm_status() failed: %s",snd_strerror(err));
return;
}
if (snd_pcm_status_get_state(status)==SND_PCM_STATE_SUSPENDED){
ms_warning("Maybe suspended, trying resume");
if ((err=snd_pcm_resume(handle))!=0){
if (err!=EWOULDBLOCK) ms_warning("snd_pcm_resume() failed: %s",snd_strerror(err));
}
}
}
static int alsa_set_params(snd_pcm_t *pcm_handle, int rw, int bits, int stereo, int rate)
{
snd_pcm_hw_params_t *hwparams=NULL;
snd_pcm_sw_params_t *swparams=NULL;
int dir;
uint exact_uvalue;
unsigned long exact_ulvalue;
int channels;
int periods=ALSA_PERIODS;
int periodsize=ALSA_PERIOD_SIZE;
int err;
int format;
/* 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(pcm_handle, hwparams) < 0) {
ms_warning("alsa_set_params: Cannot configure this PCM device.");
return -1;
}
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
ms_warning("alsa_set_params: Error setting access.");
return -1;
}
/* Set sample format */
format=SND_PCM_FORMAT_S16;
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) {
ms_warning("alsa_set_params: Error setting format.");
return -1;
}
/* Set number of channels */
if (stereo) channels=2;
else channels=1;
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) {
ms_warning("alsa_set_params: Error setting channels.");
return -1;
}
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_uvalue=rate;
dir=0;
if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_uvalue, &dir))<0){
ms_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err));
return -1;
}
if(exact_uvalue != rate)
{
exact_uvalue = 8000;
snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_uvalue, &dir);
ms_warning("alsa_set_params: The rate is not supported ==> Using %d Hz instead.", exact_uvalue);
}
/* choose greater period size when rate is high */
periodsize=periodsize*(rate/8000);
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
/* set period size */
exact_ulvalue=periodsize;
dir=0;
if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_ulvalue, &dir) < 0) {
ms_warning("alsa_set_params: Error setting period size.");
return -1;
}
if (dir != 0) {
ms_warning("alsa_set_params: The period size %d is not supported by your hardware.\n "
"==> Using %d instead.", periodsize, (int)exact_ulvalue);
}
ms_warning("alsa_set_params: periodsize:%d Using %d", periodsize, (int)exact_ulvalue);
periodsize=exact_ulvalue;
/* Set number of periods. Periods used to be called fragments. */
exact_uvalue=periods;
dir=0;
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_uvalue, &dir) < 0) {
ms_warning("alsa_set_params: Error setting periods.");
return -1;
}
ms_warning("alsa_set_params: period:%d Using %d", periods, exact_uvalue);
if (dir != 0) {
ms_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n "
"==> Using %d instead.", periods, exact_uvalue);
}
/* Apply HW parameter settings to */
/* PCM device and prepare device */
if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
ms_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err));
return -1;
}
/*prepare sw params */
if (rw){
snd_pcm_sw_params_alloca(&swparams);
snd_pcm_sw_params_current(pcm_handle, swparams);
if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){
ms_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err));
}
if ((err=snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams,periodsize*periods ))<0){
ms_warning("alsa_set_params: Error setting stop threshold:%s",snd_strerror(err));
}
if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){
ms_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err));
return -1;
}
}
return 0;
}
#ifdef EPIPE_BUGFIX
static void alsa_fill_w (snd_pcm_t *pcm_handle)
{
snd_pcm_hw_params_t *hwparams=NULL;
int channels;
snd_pcm_uframes_t buffer_size;
int buffer_size_bytes;
void *buffer;
/* Allocate the snd_pcm_hw_params_t structure on the stack. */
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_hw_params_current(pcm_handle, hwparams);
/* get channels */
snd_pcm_hw_params_get_channels (hwparams, &channels);
/* get buffer size */
snd_pcm_hw_params_get_buffer_size (hwparams, &buffer_size);
/* fill half */
buffer_size /= 2;
/* allocate buffer assuming 2 bytes per sample */
buffer_size_bytes = buffer_size * channels * 2;
buffer = alloca (buffer_size_bytes);
memset (buffer, 0, buffer_size_bytes);
/* write data */
snd_pcm_writei(pcm_handle, buffer, buffer_size);
}
#endif
static snd_pcm_t * alsa_open_r(const char *pcmdev,int bits,int stereo,int rate)
{
snd_pcm_t *pcm_handle;
int err;
ms_message("alsa_open_r: opening %s at %iHz, bits=%i, stereo=%i",pcmdev,rate,bits,stereo);
#ifndef THREADED_VERSION
if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) {
ms_warning("alsa_open_r: Error opening PCM device %s",pcmdev );
return NULL;
}
#else
/* want blocking mode for threaded version */
if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,0) < 0) {
ms_warning("alsa_open_r: Error opening PCM device %s",pcmdev );
return NULL;
}
#endif
#if 1//add by jzby 避免反复操作进来是pause状态,即使不起作用也没影响
alsa_resume(pcm_handle);
#endif
{
struct timeval tv1;
struct timeval tv2;
struct timezone tz;
int diff = 0;
err = gettimeofday(&tv1, &tz);
while (1) {
if (!(alsa_set_params(pcm_handle,0,bits,stereo,rate)<0)){
ms_message("alsa_open_r: Audio params set");
break;
}
if (!gettimeofday(&tv2, &tz) && !err) {
diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
} else {
diff = -1;
}
if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */
ms_error("alsa_open_r: Error setting params for more than 3 seconds");
snd_pcm_close(pcm_handle);
return NULL;
}
ms_warning("alsa_open_r: Error setting params (for %d micros)", diff);
usleep(200000);
}
}
err=snd_pcm_start(pcm_handle);
if (err<0){
ms_warning("snd_pcm_start() failed: %s", snd_strerror(err));
}
return pcm_handle;
}
static snd_pcm_t * alsa_open_w(const char *pcmdev,int bits,int stereo,int rate)
{
snd_pcm_t *pcm_handle;
ms_message("alsa_open_w: opening %s at %iHz, bits=%i, stereo=%i",pcmdev,rate,bits,stereo);
if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) {
ms_warning("alsa_open_w: Error opening PCM device %s",pcmdev );
return NULL;
}
alsa_resume(pcm_handle);
{
struct timeval tv1;
struct timeval tv2;
struct timezone tz;
int diff = 0;
int err;
err = gettimeofday(&tv1, &tz);
while (1) {
if (!(alsa_set_params(pcm_handle,1,bits,stereo,rate)<0)){
ms_message("alsa_open_w: Audio params set");
break;
}
if (!gettimeofday(&tv2, &tz) && !err) {
diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);
} else {
diff = -1;
}
if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */
ms_error("alsa_open_w: Error setting params for more than 3 seconds");
snd_pcm_close(pcm_handle);
return NULL;
}
ms_warning("alsa_open_w: Error setting params (for %d micros)", diff);
usleep(200000);
}
}
return pcm_handle;
}
static int alsa_can_read(snd_pcm_t *dev)
{
snd_pcm_sframes_t avail;
int err;
alsa_resume(dev);
avail = snd_pcm_avail_update(dev);
/* A buggy driver does not return an error while being in Xrun */
if (avail >= 0 && snd_pcm_state(dev) == SND_PCM_STATE_XRUN) avail=-EPIPE;
if (avail < 0) {
ms_error("snd_pcm_avail_update: %s", snd_strerror(avail)); // most probably -EPIPE
/* overrun occured, snd_pcm_state() would return SND_PCM_STATE_XRUN
FIXME: handle other error conditions*/
ms_error("*** alsa_can_read fixup, trying to recover");
snd_pcm_drain(dev); /* Ignore possible error, at least -EAGAIN.*/
err = snd_pcm_recover(dev, avail, 0);
if (err){
ms_error("snd_pcm_recover() failed with err %d: %s", err, snd_strerror(err));
return -1;
}
err = snd_pcm_start(dev);
if (err){
ms_error("snd_pcm_start() failed with err %d: %s", err, snd_strerror(err));
return -1;
}
ms_message("Recovery done");
}
return avail;
}
static int alsa_read(snd_pcm_t *handle,unsigned char *buf,int nsamples)
{
int err;
err=snd_pcm_readi(handle,buf,nsamples);
if (err<0) {
ms_warning("alsa_read: snd_pcm_readi() returned %i",err);
if (err==-EPIPE){
snd_pcm_prepare(handle);
err=snd_pcm_readi(handle,buf,nsamples);
if (err<0) ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
}else if (err==-ESTRPIPE){
alsa_resume(handle);
}else if (err!=-EWOULDBLOCK){
ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
}
}else if (err==0){
ms_warning("alsa_read: snd_pcm_readi() returned 0");
}
return err;
}
static int alsa_write(snd_pcm_t *handle,unsigned char *buf,int nsamples)
{
int err;
if ((err=snd_pcm_writei(handle,buf,nsamples))<0){
if (err==-EPIPE){
snd_pcm_prepare(handle);
#ifdef EPIPE_BUGFIX
alsa_fill_w (handle);
#endif
err=snd_pcm_writei(handle,buf,nsamples);
if (err<0) ms_warning("alsa_card_write: Error writing sound buffer (nsamples=%i):%s",nsamples,snd_strerror(err));
}else if (err==-ESTRPIPE){
alsa_resume(handle);
}else if (err!=-EWOULDBLOCK){
ms_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err));
}
}else if (err!=nsamples) {
ms_debug("Only %i samples written instead of %i",err,nsamples);
}
return err;
}
static snd_mixer_t *alsa_mixer_open(const char *mixdev){
snd_mixer_t *mixer=NULL;
int err;
err=snd_mixer_open(&mixer,0);
if (err<0){
ms_warning("Could not open alsa mixer: %s",snd_strerror(err));
return NULL;
}
if ((err = snd_mixer_attach (mixer, mixdev)) < 0){
ms_warning("Could not attach mixer to card: %s",snd_strerror(err));
snd_mixer_close(mixer);
return NULL;
}
if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){
ms_warning("snd_mixer_selem_register: %s",snd_strerror(err));
snd_mixer_close(mixer);
return NULL;
}
if ((err = snd_mixer_load (mixer)) < 0){
ms_warning("snd_mixer_load: %s",snd_strerror(err));
snd_mixer_close(mixer);
return NULL;
}
return mixer;
}
static void alsa_mixer_close(snd_mixer_t *mix){
snd_mixer_close(mix);
}
typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction;
static int get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){
long value=0;
const char *elemname;
snd_mixer_elem_t *elem;
int err;
long sndMixerPMin=0;
long sndMixerPMax=0;
long newvol=0;
elem=snd_mixer_first_elem(mixer);
while (elem!=NULL){
elemname=snd_mixer_selem_get_name(elem);
//ms_message("Found alsa mixer element %s.",elemname);
if (strcmp(elemname,name)==0){
switch (action){
case CAPTURE:
if (snd_mixer_selem_has_capture_volume(elem)){
snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol);
newvol-=sndMixerPMin;
value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
if (err<0) ms_warning("Could not get capture volume for %s:%s",name,snd_strerror(err));
//else ms_message("Successfully get capture level for %s.",elemname);
break;
}
break;
case PLAYBACK:
if (snd_mixer_selem_has_playback_volume(elem)){
snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol);
newvol-=sndMixerPMin;
value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
if (err<0) ms_warning("Could not get playback volume for %s:%s",name,snd_strerror(err));
//else ms_message("Successfully get playback level for %s.",elemname);
break;
}
break;
case CAPTURE_SWITCH:
break;
case PLAYBACK_SWITCH:
break;
}
}
elem=snd_mixer_elem_next(elem);
}
return value;
}
static void set_mixer_element(snd_mixer_t *mixer,const char *name, int level,MixerAction action){
const char *elemname;
snd_mixer_elem_t *elem;
long sndMixerPMin=0;
long sndMixerPMax=0;
long newvol=0;
elem=snd_mixer_first_elem(mixer);
while (elem!=NULL){
elemname=snd_mixer_selem_get_name(elem);
//ms_message("Found alsa mixer element %s.",elemname);
if (strcmp(elemname,name)==0){
switch(action){
case CAPTURE:
if (snd_mixer_selem_has_capture_volume(elem)){
snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
snd_mixer_selem_set_capture_volume_all(elem,newvol);
//ms_message("Successfully set capture level for %s.",elemname);
return;
}
break;
case PLAYBACK:
if (snd_mixer_selem_has_playback_volume(elem)){
snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
snd_mixer_selem_set_playback_volume_all(elem,newvol);
//ms_message("Successfully set playback level for %s.",elemname);
return;
}
break;
case CAPTURE_SWITCH:
if (snd_mixer_selem_has_capture_switch(elem)){
snd_mixer_selem_set_capture_switch_all(elem,level);
//ms_message("Successfully set capture switch for %s.",elemname);
}
break;
case PLAYBACK_SWITCH:
if (snd_mixer_selem_has_playback_switch(elem)){
snd_mixer_selem_set_playback_switch_all(elem,level);
//ms_message("Successfully set capture switch for %s.",elemname);
}
break;
}
}
elem=snd_mixer_elem_next(elem);
}
return ;
}
static void alsa_card_set_level(MSSndCard *obj,MSSndCardMixerElem e,int a)
{
snd_mixer_t *mixer;
AlsaData *ad=(AlsaData*)obj->data;
mixer=alsa_mixer_open(ad->mixdev);
if (mixer==NULL) return ;
switch(e){
case MS_SND_CARD_MASTER:
set_mixer_element(mixer,"Master",a,PLAYBACK);
break;
case MS_SND_CARD_CAPTURE:
set_mixer_element(mixer,"Capture",a,CAPTURE);
break;
case MS_SND_CARD_PLAYBACK:
set_mixer_element(mixer,"PCM",a,PLAYBACK);
break;
default:
ms_warning("alsa_card_set_level: unsupported command.");
}
alsa_mixer_close(mixer);
}
static int alsa_card_get_level(MSSndCard *obj, MSSndCardMixerElem e)
{
snd_mixer_t *mixer;
AlsaData *ad=(AlsaData*)obj->data;
int value = -1;
mixer=alsa_mixer_open(ad->mixdev);
if (mixer==NULL) return 0;
switch(e){
case MS_SND_CARD_MASTER:
value=get_mixer_element(mixer,"Master",PLAYBACK);
break;
case MS_SND_CARD_CAPTURE:
value=get_mixer_element(mixer,"Capture",CAPTURE);
break;
case MS_SND_CARD_PLAYBACK:
value=get_mixer_element(mixer,"PCM",PLAYBACK);
break;
default:
ms_warning("alsa_card_set_level: unsupported command.");
}
alsa_mixer_close(mixer);
return value;
}
static void alsa_card_set_source(MSSndCard *obj,MSSndCardCapture source)
{
snd_mixer_t *mixer;
AlsaData *ad=(AlsaData*)obj->data;
mixer=alsa_mixer_open(ad->mixdev);
if (mixer==NULL) return;
switch (source){
case MS_SND_CARD_MIC:
set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH);
set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
break;
case MS_SND_CARD_LINE:
set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH);
set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
break;
}
alsa_mixer_close(mixer);
}
static MSFilter *alsa_card_create_reader(MSSndCard *card)
{
AlsaData *ad=(AlsaData*)card->data;
MSFilter *f=ms_alsa_read_new(ad->pcmdev);
return f;
}
static MSFilter *alsa_card_create_writer(MSSndCard *card)
{
AlsaData *ad=(AlsaData*)card->data;
MSFilter *f=ms_alsa_write_new(ad->pcmdev);
return f;
}
static void alsa_card_init(MSSndCard *obj){
AlsaData *ad=ms_new0(AlsaData,1);
obj->data=ad;
}
static void alsa_card_uninit(MSSndCard *obj){
AlsaData *ad=(AlsaData*)obj->data;
if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
if (ad->mixdev!=NULL) ms_free(ad->mixdev);
ms_free(ad);
}
static void alsa_card_detect(MSSndCardManager *m){
int i;
for (i=-1;i<10;i++){
MSSndCard *card=alsa_card_new(i);
if (card!=NULL)
ms_snd_card_manager_add_card(m,card);
}
}
MSSndCardDesc alsa_card_desc={
.driver_type="ALSA",
.detect=alsa_card_detect,
.init=alsa_card_init,
.set_level=alsa_card_set_level,
.get_level=alsa_card_get_level,
.set_capture=alsa_card_set_source,
.set_control=NULL,
.get_control=NULL,
.create_reader=alsa_card_create_reader,
.create_writer=alsa_card_create_writer,
.uninit=alsa_card_uninit,
.duplicate=alsa_card_duplicate
};
static MSSndCard *alsa_card_duplicate(MSSndCard *obj){
MSSndCard *card=ms_snd_card_new(&alsa_card_desc);
AlsaData* dcard=(AlsaData*)card->data;
AlsaData* dobj=(AlsaData*)obj->data;
card->name=ms_strdup(obj->name);
card->id=ms_strdup(obj->id);
dcard->pcmdev=ms_strdup(dobj->pcmdev);
dcard->mixdev=ms_strdup(dobj->mixdev);
return card;
}
MSSndCard * ms_alsa_card_new_custom(const char *pcmdev, const char *mixdev){
MSSndCard * obj;
AlsaData *ad;
obj=ms_snd_card_new(&alsa_card_desc);
ad=(AlsaData*)obj->data;
obj->name=ms_strdup(pcmdev);
ad->pcmdev=ms_strdup(pcmdev);
ad->mixdev=ms_strdup(mixdev);
return obj;
}
static unsigned int get_card_capabilities(const char *devname){
snd_pcm_t *pcm_handle;
unsigned int ret=0;
if (snd_pcm_open(&pcm_handle,devname,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK)==0) {
ret|=MS_SND_CARD_CAP_CAPTURE;
snd_pcm_close(pcm_handle);
}
if (snd_pcm_open(&pcm_handle,devname,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK)==0) {
ret|=MS_SND_CARD_CAP_PLAYBACK;
snd_pcm_close(pcm_handle);
}
return ret;
}
static MSSndCard * alsa_card_new(int id)
{
MSSndCard * obj;
char *name=NULL;
AlsaData *ad;
int err;
if (id!=-1){
err=snd_card_get_name(id,&name);
if (err<0) {
return NULL;
}
}
obj=ms_snd_card_new(&alsa_card_desc);
ad=(AlsaData*)obj->data;
if (id==-1) {
/* the default pcm device */
obj->name=ms_strdup("default device");
ad->pcmdev=ms_strdup("default");
ad->mixdev=ms_strdup("default");
}else{
/* remove trailing spaces from card name */
char *pos1, *pos2;
pos1=ms_strdup(name);
pos2=pos1+strlen(pos1)-1;
for (; pos2>pos1 && *pos2==' '; pos2--) *pos2='\0';
obj->name=pos1;
ad->pcmdev=ms_strdup_printf("default:%i",id);
ad->mixdev=ms_strdup_printf("default:%i",id);
{
snd_mixer_t *mixer;
mixer = alsa_mixer_open(ad->mixdev);
if (mixer==NULL) {
ms_free(ad->mixdev);
ad->mixdev=ms_strdup_printf("hw:%i",id);
} else {
alsa_mixer_close(mixer);
}
}
}
/*check card capabilities: */
obj->capabilities=get_card_capabilities(ad->pcmdev);
if (obj->capabilities==0){
ms_warning("Strange, sound card %s does not seems to be capable of anything, retrying with plughw...",obj->name);
/*retry with plughw: this workarounds an alsa bug*/
ms_free(ad->pcmdev);
ad->pcmdev=ms_strdup_printf("plughw:%i",id);
obj->capabilities=get_card_capabilities(ad->pcmdev);
if (obj->capabilities==0){
ms_warning("Strange, sound card %s seems totally unusable.",obj->name);
}
}
free(name);
/*ms_message("alsa device %s found",obj->name);*/
return obj;
}
struct _AlsaReadData{
char *pcmdev;
snd_pcm_t *handle;
int rate;
int nchannels;
#ifdef THREADED_VERSION
ms_thread_t thread;
ms_mutex_t mutex;
MSBufferizer * bufferizer;
bool_t read_started;
bool_t write_started;
#endif
};
typedef struct _AlsaReadData AlsaReadData;
void alsa_read_init(MSFilter *obj){
AlsaReadData *ad=ms_new(AlsaReadData,1);
ad->pcmdev=NULL;
ad->handle=NULL;
ad->rate=8000;
ad->nchannels=1;
obj->data=ad;
#ifdef RECORDFILE
rfilefd = fopen("/media/alsaRead.pcm","w+");
#endif
#ifdef THREADED_VERSION
ad->read_started=FALSE;
ad->write_started=FALSE;
ad->bufferizer=ms_bufferizer_new();
ms_mutex_init(&ad->mutex,NULL);
ad->thread=0;
#endif
}
#ifdef THREADED_VERSION
static void * alsa_write_thread(void *p){
AlsaReadData *ad=(AlsaReadData*)p;
int samples=(160*ad->rate)/8000;
int err;
int count=0;
mblk_t *om=NULL;
struct timeval timeout;
if (ad->handle==NULL && ad->pcmdev!=NULL)
{
usleep(50*1000);
ad->handle = alsa_open_r(ad->pcmdev,16,ad->nchannels==2, ad->rate);
}
if (ad->handle==NULL) return NULL;
while (ad->read_started)
{
#if 1//add by jzby 函数参数个数原始bug
count = alsa_can_read(ad->handle);
#else
count = alsa_can_read(ad->handle,samples);
#endif
if (count==24)
{ /* keep this value for this driver */ }
else if (count<=0)
{
count = samples;
}
else if (count>0)
{
//ms_warning("%i count", count);
//count = samples;
}
int size=count*2;
om=allocb(size,0);
if ((err=alsa_read(ad->handle,om->b_wptr,count))<=0)
{
ms_warning("nothing to read");
//ms_warning("Fail to read samples %i", count);
freemsg(om); /* leak fixed */
continue;
}
//ms_warning(" read %i", err);
size=err*2;
om->b_wptr+=size;
ms_mutex_lock(&ad->mutex);
ms_bufferizer_put(ad->bufferizer,om);
ms_mutex_unlock(&ad->mutex);
if (count==24)
{
timeout.tv_sec = 0;
timeout.tv_usec = 2000;
select(0, 0, NULL, NULL, &timeout );
}
else
{
/* select will be less active than locking on "read" */
timeout.tv_sec = 0;
timeout.tv_usec = 5000;
select(0, 0, NULL, NULL, &timeout );
}
}
if (ad->handle!=NULL) snd_pcm_close(ad->handle);
ad->handle=NULL;
return NULL;
}
static void alsa_start_r(AlsaReadData *d){
if (d->read_started==FALSE){
d->read_started=TRUE;
ms_thread_create(&d->thread,NULL,alsa_write_thread,d);
}else d->read_started=TRUE;
}
static void alsa_stop_r(AlsaReadData *d){
d->read_started=FALSE;
if (d->thread!=0)
{
ms_thread_join(d->thread,NULL);
d->thread=0;
}
}
#endif
#ifdef THREADED_VERSION
void alsa_read_preprocess(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
alsa_start_r(ad);
}
#endif
void alsa_read_postprocess(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
#ifdef THREADED_VERSION
alsa_stop_r(ad);
#endif
if (ad->handle!=NULL) snd_pcm_close(ad->handle);
#ifdef RECORDFILE
fclose(rfilefd);
#endif
ad->handle=NULL;
}
void alsa_read_uninit(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
#ifdef THREADED_VERSION
alsa_stop_r(ad);
#endif
if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
if (ad->handle!=NULL) snd_pcm_close(ad->handle);
#ifdef THREADED_VERSION
ms_bufferizer_destroy(ad->bufferizer);
ms_mutex_destroy(&ad->mutex);
#endif
ms_free(ad);
}
#ifndef THREADED_VERSION
void alsa_read_process(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
int samples=(128*ad->rate)/8000;
int err;
mblk_t *om=NULL;
if (ad->handle==NULL && ad->pcmdev!=NULL){
ad->handle=alsa_open_r(ad->pcmdev,16,ad->nchannels==2,ad->rate);
}
if (ad->handle==NULL) return;
while (alsa_can_read(ad->handle)>=samples){
int size=samples*2*ad->nchannels;
om=allocb(size,0);
if ((err=alsa_read(ad->handle,om->b_wptr,samples))<=0) {
ms_warning("Fail to read samples");
freemsg(om);
return;
}
size=err*2*ad->nchannels;
om->b_wptr+=size;
/*ms_message("alsa_read_process: Outputing %i bytes",size);*/
ms_queue_put(obj->outputs[0],om);
}
}
#endif
#ifdef THREADED_VERSION
void alsa_read_process(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
mblk_t *om=NULL;
int samples=(160*ad->rate)/8000;
int size=samples*2*ad->nchannels;
int ret = 0;
ms_mutex_lock(&ad->mutex);
while (ms_bufferizer_get_avail(ad->bufferizer)>=size){
om=allocb(size,0);
ms_bufferizer_read(ad->bufferizer,om->b_wptr,size);
om->b_wptr+=size;
/*ms_message("alsa_read_process: Outputing %i bytes",size);*/
ms_queue_put(obj->outputs[0],om);
#ifdef RECORDFILE
ret = fwrite(om->b_wptr-size, 1, size, rfilefd);
if (ret != size)
{
printf("fwrite err, write size=%d, slclen=%d\n", ret, size);
}
#endif
}
ms_mutex_unlock(&ad->mutex);
}
#endif
static int alsa_read_get_sample_rate(MSFilter *obj, void *param){
AlsaReadData *ad=(AlsaReadData*)obj->data;
*((int*)param)=ad->rate;
return 0;
}
static int alsa_read_set_sample_rate(MSFilter *obj, void *param){
AlsaReadData *ad=(AlsaReadData*)obj->data;
ad->rate=*((int*)param);
return 0;
}
static int alsa_read_set_nchannels(MSFilter *obj, void *param){
AlsaReadData *ad=(AlsaReadData*)obj->data;
ad->nchannels=*((int*)param);
return 0;
}
MSFilterMethod alsa_read_methods[]={
{MS_FILTER_GET_SAMPLE_RATE, alsa_read_get_sample_rate},
{MS_FILTER_SET_SAMPLE_RATE, alsa_read_set_sample_rate},
{MS_FILTER_SET_NCHANNELS, alsa_read_set_nchannels},
{0,NULL}
};
MSFilterDesc alsa_read_desc={
.id=MS_ALSA_READ_ID,
.name="MSAlsaRead",
.text=N_("Alsa sound source"),
.category=MS_FILTER_OTHER,
.ninputs=0,
.noutputs=1,
.init=alsa_read_init,
#ifdef THREADED_VERSION
.preprocess=alsa_read_preprocess,
#endif
.process=alsa_read_process,
.postprocess=alsa_read_postprocess,
.uninit=alsa_read_uninit,
.methods=alsa_read_methods
};
static MSFilter * ms_alsa_read_new(const char *dev){
MSFilter *f=ms_filter_new_from_desc(&alsa_read_desc);
AlsaReadData *ad=(AlsaReadData*)f->data;
ad->pcmdev=ms_strdup(dev);
return f;
}
typedef struct _AlsaReadData AlsaWriteData;
#ifdef PIPE_AUDIO_OUTPUT
static void pipe_write_preprocess(MSFilter *f);
#endif
void alsa_write_init(MSFilter *obj){
AlsaWriteData *ad=ms_new(AlsaWriteData,1);
ad->pcmdev=NULL;
ad->handle=NULL;
ad->rate=8000;
ad->nchannels=1;
obj->data=ad;
#ifdef PIPE_AUDIO_OUTPUT
//pipe_write_preprocess(obj);
#endif
}
void alsa_write_postprocess(MSFilter *obj){
AlsaReadData *ad=(AlsaReadData*)obj->data;
if (ad->handle!=NULL) snd_pcm_close(ad->handle);
ad->handle=NULL;
}
void alsa_write_uninit(MSFilter *obj){
AlsaWriteData *ad=(AlsaWriteData*)obj->data;
if (ad->pcmdev!=NULL) ms_free(ad->pcmdev);
if (ad->handle!=NULL) snd_pcm_close(ad->handle);
ms_free(ad);
}
static int alsa_write_get_sample_rate(MSFilter *obj, void *data){
AlsaWriteData *ad=(AlsaWriteData*)obj->data;
*((int*)data)=ad->rate;
return 0;
}
int alsa_write_set_sample_rate(MSFilter *obj, void *data){
int *rate=(int*)data;
AlsaWriteData *ad=(AlsaWriteData*)obj->data;
ad->rate=*rate;
return 0;
}
int alsa_write_set_nchannels(MSFilter *obj, void *data){
int *n=(int*)data;
AlsaWriteData *ad=(AlsaWriteData*)obj->data;
ad->nchannels=*n;
return 0;
}
void alsa_write_process(MSFilter *obj){
AlsaWriteData *ad=(AlsaWriteData*)obj->data;
mblk_t *im=NULL;
int size;
int samples;
int err;
if (ad->handle==NULL && ad->pcmdev!=NULL){
ad->handle=alsa_open_w(ad->pcmdev,16,ad->nchannels==2,ad->rate);
#ifdef EPIPE_BUGFIX
alsa_fill_w (ad->pcmdev);
#endif
}
if (ad->handle==NULL) {
ms_queue_flush(obj->inputs[0]);
return;
}
while ((im=ms_queue_get(obj->inputs[0]))!=NULL){
while((size=im->b_wptr-im->b_rptr)>0){
samples=size/(2*ad->nchannels);
err=alsa_write(ad->handle,im->b_rptr,samples);
if (err>0) {
im->b_rptr+=err*(2*ad->nchannels);
}
else break;
}
freemsg(im);
}
}
#ifdef PIPE_AUDIO_OUTPUT
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/un.h>
typedef union
{
int word;
struct
{
unsigned char Byte0;
unsigned char Byte1;
unsigned char Byte2;
unsigned char Byte3;
} byte;
} INT4BYTE;
static char *m_audio_path = NULL;
static int pipe_write_data(const char *path, const unsigned char *ptr, const int size)
{
int len;
int sock = -1;
int i;
struct sockaddr_un sa;
if((!path) || (!ptr) || (size <0)) {
printf("error audio_pipe_write_data input path %p, ptr %p, size %d\n", path, ptr, size);
return -1;
}
sock=socket(AF_UNIX,SOCK_STREAM,0);
if(sock == -1) {
printf("%s,%d socket is error\n", __FILE__, __LINE__);
return -1;
}
sa.sun_family=AF_UNIX;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
for(i = 0; i < 10; i++){
if (!connect(sock,(struct sockaddr*)&sa,sizeof(sa))){
break;
}
printf("%s, %d, connecting to audio_server....%dsec\n", __FILE__, __LINE__, i);
sleep(1);
}
if(i == 10){
close(sock);
printf("%s,%d connect to audio_server error\n", __FILE__, __LINE__);
return -1;
}
len = write(sock, ptr, size);
if( len == -1) {
printf("%s,%d write is error\n", __FILE__, __LINE__);
}
close(sock);
return len;
}
static void pipe_write_preprocess(MSFilter *f){
AlsaWriteData *ad=(AlsaWriteData*)f->data;
unsigned char buf[10];
INT4BYTE tmp;
bool_t stereo = ((ad->nchannels)==2);
#ifdef RECORDFILE
tempFd = open("/mnt/tmpWriteData.pcm", O_WRONLY | O_CREAT | O_TRUNC);
if(tempFd < 0){
printf("open /mnt/tmpWriteData.pcm failed: %s",strerror(errno));
}
#endif
m_audio_path = strdup("/tmp/remote-audio");
buf[0] = 0xFF;
buf[1] = stereo +1;
tmp.word = 16;
buf[2] = tmp.byte.Byte0;
buf[3] = tmp.byte.Byte1;
buf[4] = tmp.byte.Byte2;
buf[5] = tmp.byte.Byte3;
tmp.word = ad->rate;
buf[6] = tmp.byte.Byte0;
buf[7] = tmp.byte.Byte1;
buf[8] = tmp.byte.Byte2;
buf[9] = tmp.byte.Byte3;
if(pipe_write_data(m_audio_path, buf, sizeof(buf)) == -1 ) {
printf("%s,%d, audio_pipe_write_data is error\n", __FILE__, __LINE__);
}
}
static void pipe_write_postprocess(MSFilter *f){
free(m_audio_path);
m_audio_path = NULL;
#ifdef RECORDFILE
close(tempFd);
#endif
}
static void pipe_write_process(MSFilter *f){
mblk_t *im = NULL;
int size;
while((im=ms_queue_get(f->inputs[0]))!=NULL){
size=im->b_wptr-im->b_rptr;
if(size > 0) {
#ifdef BLUE_TEST
if(use_bluetooth_flag)
{
samples=size/(2*ad->nchannels); //im->b_rptr
pthread_mutex_lock(&use_bluetooth_mutex);
if(use_bluetooth_play)
{
samples=size/(2*ad->nchannels);
err=alsa_write(ad->handle,im->b_rptr,samples);
printf("\e[34m [ %s | %d ] Write 2 \e[0m\n", __FUNCTION__, __LINE__);
}
use_bluetooth_play = FALSE;
pthread_mutex_unlock(&use_bluetooth_mutex);
if (err>0) {
im->b_rptr+=err*(2*ad->nchannels);
}
else break;
}
else
#endif
{
if(pipe_write_data(m_audio_path, im->b_rptr, size) == -1 ) {
printf("%s,%d, audio_pipe_write_data is error\n", __FILE__, __LINE__);
}
#ifdef RECORDFILE
write(tempFd,im->b_rptr,size);
#endif
}
freemsg(im);
}
}
}
#endif
MSFilterMethod alsa_write_methods[]={
{MS_FILTER_GET_SAMPLE_RATE, alsa_write_get_sample_rate},
{MS_FILTER_SET_SAMPLE_RATE, alsa_write_set_sample_rate},
{MS_FILTER_SET_NCHANNELS, alsa_write_set_nchannels},
{0,NULL}
};
#ifdef PIPE_AUDIO_OUTPUT
MSFilterDesc alsa_write_desc={
.id=MS_ALSA_WRITE_ID,
.name="MSAlsaWrite",
.text=N_("Alsa sound output"),
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=0,
.init=alsa_write_init,
.preprocess=pipe_write_preprocess,
.process=pipe_write_process,
.postprocess=pipe_write_postprocess,
.uninit=alsa_write_uninit,
.methods=alsa_write_methods
};
#else
MSFilterDesc alsa_write_desc={
.id=MS_ALSA_WRITE_ID,
.name="MSAlsaWrite",
.text=N_("Alsa sound output"),
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=0,
.init=alsa_write_init,
.process=alsa_write_process,
.postprocess=alsa_write_postprocess,
.uninit=alsa_write_uninit,
.methods=alsa_write_methods
};
#endif
static MSFilter * ms_alsa_write_new(const char *dev){
MSFilter *f=ms_filter_new_from_desc(&alsa_write_desc);
AlsaWriteData *ad=(AlsaWriteData*)f->data;
ad->pcmdev=ms_strdup(dev);
return f;
}
MS_FILTER_DESC_EXPORT(alsa_write_desc)
MS_FILTER_DESC_EXPORT(alsa_read_desc)
这篇关于alsa音频采集和播放的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!