您的位置:首页 > 编程语言

ALSA 接口编程实例——语音聊天

2017-10-01 11:00 495 查看
本文转自博客:http://blog.csdn.net/u014338577/article/details/49096397

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

/*

本程序维护一个 256bytes*4 缓冲区,两个信号量保护(读和写)。创建两

个线程,一个用于采集声卡数据并写到缓冲区,数据采集线程使用ALSA接口

编程,设置采样率 22333,周期帧数 128,帧格式 U8,声道数 2,每个周期

大约 5.73ms,每个周期 256bytes。另外一个将缓冲区数据广播到网络,每

次发送 256bytes。

*/

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

#include <unistd.h>

#include <pthread.h>

#include <stdlib.h>

#include <semaphore.h>

#include <sys/types.h> /* basic system data types */

#include <sys/socket.h> /* basic socket definitions */

#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */

#include <arpa/inet.h> /* inet(3) functions */

#define RATE 22333

#define CHANNEL 2

#define FORMAT SND_PCM_FORMAT_U8

#define FRAMES 128

#define SIZE CHANNEL*FRAMES*1

#define NBUFF 4

// 套接字端口

#define PORT 10000

#define SA struct sockaddr

// 数据缓冲区及信号量

struct {

char buffer[1024];

sem_t mutex, nempty, nstored;

} shared;

char* pRead = shared.buffer; //读指针

char* pWrite = shared.buffer; //写指针

void* sendData(void *arg); //线程函数,广播数据

void* generateData(void *arg); //线程函数,读声卡

// 计数变量

long produce=0;

long consume=0;

long totalTime = 0;

int main()

{

pthread_t tid_generateData, tid_sendData;

// 初始化信号量

sem_init(&shared.mutex, 0, 1); //未用到

sem_init(&shared.nempty, 0, NBUFF);

sem_init(&shared.nstored, 0, 0);

// 创建读声卡线程,将数据保存到缓冲区

pthread_create(&tid_generateData, NULL, generateData, NULL);

// 创建广播线程,将缓冲区数据发送到网络

pthread_create(&tid_sendData, NULL, sendData, NULL);

pthread_join(tid_sendData, NULL);

pthread_join(tid_generateData, NULL);

sem_destroy(&shared.mutex);

sem_destroy(&shared.nempty);

sem_destroy(&shared.nstored);

exit(0);

}

void* sendData(void *arg)

{

int sockfd;

struct sockaddr_in servaddr;

/* socket 初始化 */

const int on = 1;

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(PORT);

inet_pton(AF_INET, "192.168.1.255", &servaddr.sin_addr);

sockfd = socket(AF_INET, SOCK_DGRAM, 0);

setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

int n;

const SA *pservaddr = (SA*)(&servaddr);

socklen_t servlen = sizeof(SA);

printf("\n\n\n\n\nData generating starts, Broadcasting ...\n\n\n\n\n\n\n\n\n\n");

printf("|------------------------------------------------------------|\t0\tmin |\n\033[1A|");

while(1)

{

// 获取nstored信号量

sem_wait(&shared.nstored);

// 发送数据

n = sendto(sockfd, pRead, 256, 0, pservaddr, servlen);

if(n!=256) //printf("send short: send %d\n",n)

{

sem_post(&shared.nstored);

continue;

}

// 更新缓冲区读指针

pRead += 256;

if(pRead-1024 == shared.buffer)

pRead = shared.buffer;

// 释放nempty信号量

sem_post(&shared.nempty);

// 计数器

if(0 == ++consume % 175)

{

++totalTime;

printf("-");

fflush(stdout);

if(0 == totalTime %60)

printf("|\t%ld\tmin |\n\033[1A|", totalTime/60),fflush(stdout);

}

}

}

void* generateData(void *arg)

{

// 设备打开初始化配置

int rc;

snd_pcm_t *handle;

snd_pcm_hw_params_t *params;

unsigned int val = RATE; // 采样率 22333

int dir;

snd_pcm_uframes_t frames;

rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_CAPTURE, 0); // 打开方式为“抓取数据”

if (rc < 0)

{

fprintf(stdout, "unable to open pcm device: %s\n",snd_strerror(rc));

exit(1);

}

snd_pcm_hw_params_alloca(¶ms);

snd_pcm_hw_params_any(handle, params);

snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);

snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_U8); //帧数据格式,每帧1byte

snd_pcm_hw_params_set_channels(handle, params, 2); // 声道数 2

snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); // 获取真实采样率 22333

snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); // 获取每周期帧数 64

rc = snd_pcm_hw_params(handle, params);

if (rc < 0)

{

fprintf(stdout,"unable to set hw parameters: %s\n", snd_strerror(rc));

exit(1);

}

snd_pcm_hw_params_get_period_size(params, &frames, &dir);

snd_pcm_hw_params_get_period_time(params, &val, &dir);

while (1)

{

frames = FRAMES; // 更改每周期的帧数,设置为128

sem_wait(&shared.nempty);

rc = snd_pcm_readi(handle, pWrite, frames); // 获取256 Bytes 数据

if (rc != (int)frames)

{

usleep(1000);

snd_pcm_prepare(handle);

sem_post(&shared.nempty);

continue;

}

// 更新缓冲区写指针

pWrite += 256;

if(pWrite-1024 == shared.buffer)

pWrite = shared.buffer;

sem_post(&shared.nstored);

// 计数器

++produce;

}

snd_pcm_drain(handle);

snd_pcm_close(handle);

}

[cpp] view
plaincopy

/*

本程序维护一个256bytes*4缓冲区,两个信号量保护(读和写)。创建两

个线程,一个监听广播消息并将获取的数据写到缓冲区,另外一个线程将

缓冲区数据写到声卡。写声卡编程使用ALSA接口编程,采样率 22333,周

期帧数128,帧格式U8,声道数2,计算下来,每个周期大约 5.73ms,每个

周期256bytes。

*/

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

#include <unistd.h>

#include <pthread.h>

#include <stdlib.h>

#include <semaphore.h>

#include <sys/types.h> /* basic system data types */

#include <sys/socket.h> /* basic socket definitions */

#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */

#include <arpa/inet.h> /* inet(3) functions */

// 声卡配置参数

#define RATE 22333

#define CHANNEL 2

#define FORMAT SND_PCM_FORMAT_U8

#define FRAMES 128

#define SIZE CHANNEL*FRAMES*1

#define NBUFF 4

#define SEM_MUTEX "mutex"

#define SEM_NEMPTY "nempty"

#define SEM_NSTORED "nstored"

#define PORT 10000

#define SA struct sockaddr

// 缓冲区及信号量

struct {

char buffer[1024];

sem_t mutex, nempty, nstored;

} shared;

// 缓冲区读写指针

char* pRead = shared.buffer;

char* pWrite = shared.buffer;

void* recvData(void *arg);

void* generateSnd(void *arg);

// 计数变量

long produce=0;

long consume=0;

long totalTime = 0;

int main()

{

pthread_t tid_generateSnd, tid_recvData;

// 信号量初始化

sem_init(&shared.mutex, 0, 1); //未使用

sem_init(&shared.nempty, 0, NBUFF);

sem_init(&shared.nstored, 0, 0);

// 创建发声线程

pthread_create(&tid_generateSnd, NULL, generateSnd, NULL);

// 创建数据接收线程

pthread_create(&tid_recvData, NULL, recvData, NULL);

pthread_join(tid_recvData, NULL);

pthread_join(tid_generateSnd, NULL);

sem_destroy(&shared.mutex);

sem_destroy(&shared.nempty);

sem_destroy(&shared.nstored);

exit(0);

}

void* recvData(void *arg)

{

// 配置socket

int sockfd;

struct sockaddr_in servaddr;

/* initialization for socket */

const int on = 1;

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(PORT);

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

sockfd = socket(AF_INET, SOCK_DGRAM, 0);

bind(sockfd, (SA *) &servaddr, sizeof(servaddr));

setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

int n;

SA *preply_addr = (SA*)malloc(sizeof(SA));

socklen_t len = sizeof(SA);

// 计时器

printf("\n\n\n\n\nData receiving starts, voice generating ...\n\n\n\n\n\n\n\n\n\n");

printf("|------------------------------------------------------------|\t0\tmin |\n\033[1A|");

for (;;)

{

// 获取写信号量

sem_wait(&shared.nempty);

// 监听网络,并将数据写到缓冲区

n = recvfrom(sockfd, pWrite, 256, 0, preply_addr, &len);

if (n < 0) {

if (errno == EINTR)

break; /* waited long enough for replies */

}

else if(n != 256)

{

sem_post(&shared.nempty);

continue;

}

else

{

// 更新写指针

pWrite += 256;

if(pWrite-1024 == shared.buffer)

pWrite = shared.buffer;

// 释放读信号量

sem_post(&shared.nstored);

if(0 == ++produce % 175)

{

++totalTime;

printf("-");

fflush(stdout);

if(0 == totalTime %60)

printf("|\t%ld\tmin |\n\033[1A|", totalTime/60),fflush(stdout);

}

}

}

free(preply_addr);

}

void* generateSnd(void *arg)

{

// 声卡配置变量

int rc;

snd_pcm_t *handle;

snd_pcm_hw_params_t *params;

unsigned int val = RATE;

int dir;

snd_pcm_uframes_t frames;

rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_PLAYBACK, 0); // 播放模式打开

if (rc < 0)

{

fprintf(stderr, "unable to open pcm device: %s\n",snd_strerror(rc));

exit(1);

}

// 配置声卡,和发送进程的声卡配置一致

snd_pcm_hw_params_alloca(¶ms);

snd_pcm_hw_params_any(handle, params);

snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);

snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_U8);

snd_pcm_hw_params_set_channels(handle, params, 2);

snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

rc = snd_pcm_hw_params(handle, params);

if (rc < 0)

{

fprintf(stderr,"unable to set hw parameters: %s\n", snd_strerror(rc));

exit(1);

}

snd_pcm_hw_params_get_period_size(params, &frames, &dir);

snd_pcm_hw_params_get_period_time(params, &val, &dir);

while (1)

{

frames = FRAMES;

// 获取读信号量

sem_wait(&shared.nstored);

// 向声卡写数据

rc = snd_pcm_writei(handle, pRead, frames);

if (rc != (int)frames)

{

usleep(1000);

snd_pcm_prepare(handle);

sem_post(&shared.nstored);

continue;

}

// 更新读指针

pRead += 256;

if(pRead-1024 == shared.buffer)

pRead = shared.buffer;

// 释放写信号量

sem_post(&shared.nempty);

++consume;

}

snd_pcm_drain(handle);

snd_pcm_close(handle);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  音频