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

http://blog.csdn.net/leon_unique/article/details/9400957#comments

[cpp]
view plain copy
  1. /*  
  2. 本程序维护一个 256bytes*4 缓冲区,两个信号量保护(读和写)。创建两 
  3. 个线程,一个用于采集声卡数据并写到缓冲区,数据采集线程使用ALSA接口 
  4. 编程,设置采样率 22333,周期帧数 128,帧格式 U8,声道数 2,每个周期 
  5. 大约 5.73ms,每个周期 256bytes。另外一个将缓冲区数据广播到网络,每 
  6. 次发送 256bytes。 
  7. */  
  8.   
  9. #define ALSA_PCM_NEW_HW_PARAMS_API   
  10.   
  11. #include <alsa/asoundlib.h>  
  12. #include <unistd.h>  
  13. #include <pthread.h>  
  14. #include <stdlib.h>  
  15. #include <semaphore.h>  
  16. #include <sys/types.h>    /* basic system data types */  
  17. #include <sys/socket.h>   /* basic socket definitions */  
  18. #include <netinet/in.h>   /* sockaddr_in{} and other Internet defns */  
  19. #include <arpa/inet.h>    /* inet(3) functions */  
  20.   
  21. #define RATE 22333  
  22. #define CHANNEL 2  
  23. #define FORMAT  SND_PCM_FORMAT_U8  
  24. #define FRAMES  128  
  25.   
  26. #define SIZE CHANNEL*FRAMES*1  
  27.   
  28. #define NBUFF    4    
  29.   
  30. // 套接字端口  
  31. #define PORT 10000    
  32. #define SA  struct sockaddr  
  33.   
  34. // 数据缓冲区及信号量  
  35. struct {      
  36.     char   buffer[1024];    
  37.     sem_t mutex, nempty, nstored;    
  38. } shared;  
  39.   
  40. char* pRead = shared.buffer; //读指针  
  41. char* pWrite = shared.buffer; //写指针  
  42. void* sendData(void *arg); //线程函数,广播数据  
  43. void* generateData(void *arg); //线程函数,读声卡  
  44.   
  45. // 计数变量  
  46. long produce=0;  
  47. long consume=0;  
  48. long totalTime = 0;  
  49.   
  50. int main()   
  51. {     
  52.     pthread_t   tid_generateData, tid_sendData;  
  53.   
  54.     // 初始化信号量  
  55.     sem_init(&shared.mutex, 0, 1);  //未用到  
  56.     sem_init(&shared.nempty, 0, NBUFF);    
  57.     sem_init(&shared.nstored, 0, 0);    
  58.      
  59.     // 创建读声卡线程,将数据保存到缓冲区  
  60.     pthread_create(&tid_generateData, NULL, generateData, NULL);  
  61.     // 创建广播线程,将缓冲区数据发送到网络  
  62.     pthread_create(&tid_sendData, NULL, sendData, NULL);    
  63.     
  64.     pthread_join(tid_sendData, NULL);    
  65.     pthread_join(tid_generateData, NULL);    
  66.     
  67.     sem_destroy(&shared.mutex);    
  68.     sem_destroy(&shared.nempty);    
  69.     sem_destroy(&shared.nstored);    
  70.     exit(0);    
  71. }   
  72.   
  73. void* sendData(void *arg)  
  74. {  
  75.     int         sockfd;  
  76.     struct      sockaddr_in     servaddr;  
  77.       
  78.     /* socket 初始化 */  
  79.     const int on = 1;  
  80.     bzero(&servaddr, sizeof(servaddr));  
  81.     servaddr.sin_family = AF_INET;  
  82.     servaddr.sin_port = htons(PORT);  
  83.     inet_pton(AF_INET, "192.168.1.255", &servaddr.sin_addr);  
  84.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
  85.     setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));  
  86.       
  87.     int n;  
  88.     const SA *pservaddr = (SA*)(&servaddr);   
  89.     socklen_t servlen = sizeof(SA);  
  90.     printf("\n\n\n\n\nData generating starts, Broadcasting ...\n\n\n\n\n\n\n\n\n\n");  
  91.     printf("|------------------------------------------------------------|\t0\tmin |\n\033[1A|");  
  92.     while(1)  
  93.     {  
  94.         // 获取nstored信号量  
  95.         sem_wait(&shared.nstored);  
  96.           
  97.         // 发送数据  
  98.         n = sendto(sockfd, pRead, 256, 0, pservaddr, servlen);  
  99.         if(n!=256) //printf("send short: send %d\n",n)  
  100.         {  
  101.             sem_post(&shared.nstored);  
  102.             continue;  
  103.         }  
  104.           
  105.         // 更新缓冲区读指针  
  106.         pRead += 256;  
  107.         if(pRead-1024 == shared.buffer)  
  108.             pRead = shared.buffer;  
  109.               
  110.         // 释放nempty信号量  
  111.         sem_post(&shared.nempty);  
  112.           
  113.         // 计数器  
  114.         if(0 == ++consume % 175)  
  115.         {  
  116.             ++totalTime;  
  117.             printf("-");  
  118.             fflush(stdout);  
  119.             if(0 == totalTime %60)  
  120.                 printf("|\t%ld\tmin |\n\033[1A|", totalTime/60),fflush(stdout);  
  121.         }  
  122.     }  
  123. }  
  124.   
  125. void* generateData(void *arg)  
  126. {  
  127.     // 设备打开初始化配置  
  128.     int rc;    
  129.     snd_pcm_t *handle;   
  130.     snd_pcm_hw_params_t *params;   
  131.     unsigned int val = RATE;  // 采样率 22333  
  132.     int dir;   
  133.     snd_pcm_uframes_t frames;    
  134.        
  135.     rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_CAPTURE, 0); // 打开方式为“抓取数据”  
  136.       
  137.     if (rc < 0)   
  138.     {  
  139.         fprintf(stdout, "unable to open pcm device: %s\n",snd_strerror(rc));  
  140.         exit(1);   
  141.     }    
  142.     snd_pcm_hw_params_alloca(¶ms);   
  143.     snd_pcm_hw_params_any(handle, params);    
  144.     snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);  
  145.     snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_U8); //帧数据格式,每帧1byte  
  146.     snd_pcm_hw_params_set_channels(handle, params, 2);              // 声道数 2  
  147.     snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);    // 获取真实采样率 22333  
  148.     snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); // 获取每周期帧数 64  
  149.     rc = snd_pcm_hw_params(handle, params);   
  150.     if (rc < 0)  
  151.     {       
  152.         fprintf(stdout,"unable to set hw parameters: %s\n", snd_strerror(rc));   
  153.         exit(1);   
  154.     }    
  155.     snd_pcm_hw_params_get_period_size(params, &frames, &dir);   
  156.     snd_pcm_hw_params_get_period_time(params, &val, &dir);  
  157.   
  158.     while (1)   
  159.     {       
  160.         frames = FRAMES; // 更改每周期的帧数,设置为128  
  161.         sem_wait(&shared.nempty);  
  162.           
  163.         rc = snd_pcm_readi(handle, pWrite, frames); // 获取256 Bytes 数据  
  164.           
  165.         if (rc != (int)frames)   
  166.         {  
  167.             usleep(1000);     
  168.             snd_pcm_prepare(handle);  
  169.             sem_post(&shared.nempty);  
  170.             continue;  
  171.         }  
  172.   
  173.         // 更新缓冲区写指针  
  174.         pWrite += 256;  
  175.         if(pWrite-1024 == shared.buffer)  
  176.             pWrite = shared.buffer;  
  177.         sem_post(&shared.nstored);  
  178.         // 计数器  
  179.         ++produce;  
  180.     }    
  181.     snd_pcm_drain(handle);   
  182.     snd_pcm_close(handle);  
  183. }  

  1. /*  
  2. 本程序维护一个256bytes*4缓冲区,两个信号量保护(读和写)。创建两 
  3. 个线程,一个监听广播消息并将获取的数据写到缓冲区,另外一个线程将 
  4. 缓冲区数据写到声卡。写声卡编程使用ALSA接口编程,采样率 22333,周 
  5. 期帧数128,帧格式U8,声道数2,计算下来,每个周期大约 5.73ms,每个 
  6. 周期256bytes。 
  7. */  
  8.   
  9. #define ALSA_PCM_NEW_HW_PARAMS_API   
  10.   
  11. #include <alsa/asoundlib.h>  
  12. #include <unistd.h>  
  13. #include <pthread.h>  
  14. #include <stdlib.h>  
  15. #include <semaphore.h>  
  16. #include    <sys/types.h> /* basic system data types */  
  17. #include    <sys/socket.h>    /* basic socket definitions */  
  18. #include    <netinet/in.h>    /* sockaddr_in{} and other Internet defns */  
  19. #include    <arpa/inet.h> /* inet(3) functions */  
  20.   
  21. // 声卡配置参数  
  22. #define RATE 22333  
  23. #define CHANNEL 2  
  24. #define FORMAT  SND_PCM_FORMAT_U8  
  25. #define FRAMES  128  
  26.   
  27. #define SIZE CHANNEL*FRAMES*1  
  28.   
  29. #define NBUFF    4    
  30. #define SEM_MUTEX   "mutex"         
  31. #define SEM_NEMPTY  "nempty"    
  32. #define SEM_NSTORED "nstored"  
  33.   
  34. #define PORT 10000  
  35. #define SA  struct sockaddr  
  36.   
  37. // 缓冲区及信号量  
  38. struct {      
  39.   char   buffer[1024];    
  40.   sem_t mutex, nempty, nstored;    
  41. } shared;  
  42.   
  43. // 缓冲区读写指针  
  44. char* pRead = shared.buffer;  
  45. char* pWrite = shared.buffer;  
  46. void* recvData(void *arg);  
  47. void* generateSnd(void *arg);  
  48.   
  49. // 计数变量  
  50. long produce=0;  
  51. long consume=0;  
  52. long totalTime = 0;  
  53.   
  54. int main()   
  55. {     
  56.     pthread_t   tid_generateSnd, tid_recvData;   
  57.   
  58.     // 信号量初始化  
  59.     sem_init(&shared.mutex, 0, 1);  //未使用  
  60.     sem_init(&shared.nempty, 0, NBUFF);    
  61.     sem_init(&shared.nstored, 0, 0);    
  62.      
  63.     // 创建发声线程  
  64.     pthread_create(&tid_generateSnd, NULL, generateSnd, NULL);    
  65.     // 创建数据接收线程  
  66.     pthread_create(&tid_recvData, NULL, recvData, NULL);    
  67.     
  68.     pthread_join(tid_recvData, NULL);    
  69.     pthread_join(tid_generateSnd, NULL);    
  70.     
  71.     sem_destroy(&shared.mutex);    
  72.     sem_destroy(&shared.nempty);    
  73.     sem_destroy(&shared.nstored);    
  74.     exit(0);    
  75. }   
  76.   
  77. void* recvData(void *arg)  
  78. {  
  79.     // 配置socket  
  80.     int         sockfd;  
  81.     struct      sockaddr_in     servaddr;  
  82.     /* initialization for socket */  
  83.     const int on = 1;  
  84.     bzero(&servaddr, sizeof(servaddr));  
  85.     servaddr.sin_family = AF_INET;  
  86.     servaddr.sin_port = htons(PORT);  
  87.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  88.     sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
  89.     bind(sockfd, (SA *) &servaddr, sizeof(servaddr));  
  90.     setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));  
  91.       
  92.     int n;  
  93.     SA  *preply_addr = (SA*)malloc(sizeof(SA));  
  94.     socklen_t len = sizeof(SA);  
  95.       
  96.     // 计时器  
  97.     printf("\n\n\n\n\nData receiving starts, voice generating ...\n\n\n\n\n\n\n\n\n\n");  
  98.     printf("|------------------------------------------------------------|\t0\tmin |\n\033[1A|");  
  99.       
  100.     for (;;)   
  101.     {  
  102.         // 获取写信号量  
  103.         sem_wait(&shared.nempty);  
  104.           
  105.         // 监听网络,并将数据写到缓冲区  
  106.         n = recvfrom(sockfd, pWrite, 256, 0, preply_addr, &len);  
  107.         if (n < 0) {  
  108.             if (errno == EINTR)  
  109.                 break;      /* waited long enough for replies */  
  110.         }   
  111.         else if(n != 256)   
  112.         {  
  113.             sem_post(&shared.nempty);  
  114.             continue;  
  115.         }  
  116.         else  
  117.         {  
  118.             // 更新写指针  
  119.             pWrite += 256;  
  120.             if(pWrite-1024 == shared.buffer)  
  121.                 pWrite = shared.buffer;  
  122.             // 释放读信号量  
  123.             sem_post(&shared.nstored);  
  124.             if(0 == ++produce % 175)  
  125.             {  
  126.                 ++totalTime;  
  127.                 printf("-");  
  128.                 fflush(stdout);  
  129.                 if(0 == totalTime %60)  
  130.                     printf("|\t%ld\tmin |\n\033[1A|", totalTime/60),fflush(stdout);  
  131.             }  
  132.         }  
  133.     }  
  134.     free(preply_addr);  
  135. }  
  136.   
  137. void* generateSnd(void *arg)  
  138. {  
  139.     // 声卡配置变量  
  140.     int rc;    
  141.     snd_pcm_t *handle;   
  142.     snd_pcm_hw_params_t *params;   
  143.     unsigned int val = RATE;   
  144.     int dir;   
  145.     snd_pcm_uframes_t frames;    
  146.       
  147.     rc = snd_pcm_open(&handle, "default",SND_PCM_STREAM_PLAYBACK, 0); // 播放模式打开  
  148.       
  149.     if (rc < 0)   
  150.     {  
  151.         fprintf(stderr, "unable to open pcm device: %s\n",snd_strerror(rc));  
  152.         exit(1);   
  153.     }    
  154.       
  155.     // 配置声卡,和发送进程的声卡配置一致  
  156.     snd_pcm_hw_params_alloca(¶ms);   
  157.     snd_pcm_hw_params_any(handle, params);    
  158.     snd_pcm_hw_params_set_access(handle, params,SND_PCM_ACCESS_RW_INTERLEAVED);  
  159.     snd_pcm_hw_params_set_format(handle, params,SND_PCM_FORMAT_U8);   
  160.     snd_pcm_hw_params_set_channels(handle, params, 2);   
  161.     snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);   
  162.     snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);   
  163.     rc = snd_pcm_hw_params(handle, params);   
  164.     if (rc < 0)  
  165.     {       
  166.         fprintf(stderr,"unable to set hw parameters: %s\n", snd_strerror(rc));   
  167.         exit(1);   
  168.     }    
  169.     snd_pcm_hw_params_get_period_size(params, &frames, &dir);   
  170.     snd_pcm_hw_params_get_period_time(params, &val, &dir);  
  171.   
  172.     while (1)   
  173.     {       
  174.         frames = FRAMES;  
  175.         // 获取读信号量  
  176.         sem_wait(&shared.nstored);  
  177.       
  178.         // 向声卡写数据  
  179.         rc = snd_pcm_writei(handle, pRead, frames);  
  180.         if (rc != (int)frames)   
  181.         {  
  182.             usleep(1000);  
  183.             snd_pcm_prepare(handle);  
  184.             sem_post(&shared.nstored);  
  185.             continue;  
  186.         }  
  187.           
  188.         // 更新读指针  
  189.         pRead += 256;  
  190.         if(pRead-1024 == shared.buffer)  
  191.             pRead = shared.buffer;  
  192.         // 释放写信号量  
  193.         sem_post(&shared.nempty);  
  194.         ++consume;  
  195.     }    
  196.     snd_pcm_drain(handle);   
  197.     snd_pcm_close(handle);  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值