xcgiao 2022-10-10 22:09 采纳率: 100%
浏览 57
已结题

ALSA 用udp传输两个音源到服务端,使得服务端同时播放两个声音

问题遇到的现象和发生背景

我已经写好一对一的客户端和服务器的实时语音传输,接下来补充就好了,希望能帮我解答

用代码块功能插入代码,请勿粘贴截图
server端
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
    int ret;
    snd_pcm_t *handle;//pcm句柄
    snd_pcm_hw_params_t *params;//pcm属性
    snd_pcm_sw_params_t *swparams;
    snd_pcm_uframes_t period_frames;

    FILE *fp=NULL;
    char comm_ret[100]={'0'};
    fp=popen("amixer aset numid=17,iface=MIXER,name='Speaker Function' 0","r");//关闭扬声器,音量17
    if(fp==NULL)
    {
        printf("shell retor\n");
        return 0;
    }
    while(fgets(comm_ret,sizeof(comm_ret) - 1,fp)!=NULL)
        printf("amixer:\n%s\n",comm_ret);
    pclose(fp);

    //打开设备,播放模式
    int r = snd_pcm_open(&handle, "hw:1,0", SND_PCM_STREAM_PLAYBACK,0);
    if(r < 0)
    {
        perror("snd pcm open fail");
        return -1;
    }


    snd_pcm_hw_params_alloca(¶ms);//分配参数结构体
    snd_pcm_sw_params_alloca(&swparams);
    snd_pcm_hw_params_any(handle, params);//设置默认参数,初始化
    //交错模式
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

    //设置双声道,小端格式,有符号16位
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(handle, params, 2);

    //设置采样率
    int  rate = 44100;
    snd_pcm_hw_params_set_rate_near(handle,params,&rate,0);
    snd_pcm_prepare(handle);

    //设置好的参数回写设备
    r = snd_pcm_hw_params(handle, params);
    if(r < 0)
    {
        perror("snd pcm params fail");
        return -1;
    }

/*    unsigned buffer_time=0;
    unsigned period_time=0;
    //snd_pcm_hw_params_get_buffer_max(params,&buffer,0);//
    if(buffer_time>500000)
        buffer_time=500000;
    if (period_time > 0)
        ret = snd_pcm_hw_params_set_period_time_near(handle, params,&period_time, 0);
    else
        ret = snd_pcm_hw_params_set_period_size_near(handle, params,&period_frames, 0);
*/
    //定义帧数
    int frames;
    //获取一个周期有多少帧数据,一个周期一个周期方式处理音频数据。
    snd_pcm_hw_params_get_period_size(params,(snd_pcm_uframes_t*)&frames,0);

    //    snd_pcm_prepare(handle);

    int size=frames*4;
    unsigned char *buffer = malloc(size);

    //    snd_pcm_hw_params_get_buffer_size(params,(snd_pcm_uframes_t*)&buffer);

    snd_pcm_sw_params_current(handle,swparams);

    snd_pcm_sw_params_set_start_threshold(handle,swparams,0);//让设备立即启动    
    snd_pcm_sw_params_set_stop_threshold(handle,swparams,size);//读完size大小的数据后就停止

    //初始化网络
    struct sockaddr_in seraddr,cliaddr;
    int addrlen=sizeof(struct sockaddr);
    int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
    memset(&seraddr, 0, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(8888);
    seraddr.sin_addr.s_addr=INADDR_ANY;//本地IP

    //绑定
    ret = bind(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("bind fail");
        return -1;
    }
    else
    {
        printf("bind success!\n");
    }

    while(1)
    {
        usleep(2000);
        //接收数据
        ret = recvfrom(sockfd, buffer, size, 0, (struct sockaddr *)&cliaddr, &addrlen);
        if(ret <= 0){break;}

        if(ret<frames*2)
        {
            printf("finish writing!\n");
            break;
        }
        printf("recv:%d  ", ret);
        snd_pcm_format_set_silence(SND_PCM_FORMAT_S16_LE,&buffer,0);
        usleep(2000);
        ret=snd_pcm_writei(handle,buffer,frames);
        if(ret<0)
        {
            if(ret==-EPIPE)
            {
                usleep(2000);
                fprintf(stderr,"<<<<<<<<<<Buffer Underrun>>>>>>>>>>\n");//添加一个sleep,防止数据来不及读取,让系统有时间准备更多的数据
                snd_pcm_prepare(handle);
            }
            else if(ret==-ESTRPIPE)
            {
                fprintf(stderr,"<<<<<<<<<<writei retor -ESTRPIPE>>>>>>>>>>\n");
            }
            else if(ret==-EBADFD)
            {
                fprintf(stderr,"<<<<<<<<<<writei retor -EABDFD>>>>>>>>>>\n");
            }
        }
        printf("write ret=%d\n",ret);
        bzero(buffer,sizeof(buffer));
    }
    //关闭
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    return 0;
}

服务端
#include <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>


int main()
{
    int ret=0;
    snd_pcm_t *handle;//pcm句柄
    snd_pcm_hw_params_t *params;//pcm属性
    snd_pcm_sw_params_t *swparams;

    FILE *fp=NULL;
    char comm_ret[100] = {'0'};
    fp=popen("amixer cset numid=10,iface=MIXER,name='Audio main mic' 1","r");
    if(fp==NULL)
    {
        printf("shell error\n");
        return 0;
    }
    while(fgets(comm_ret,sizeof(comm_ret) - 1,fp)!=NULL)
        printf("amixer:\n%s\n",comm_ret);
    pclose(fp);

    //打开设备
    int r = snd_pcm_open(&handle, "hw:1,0", SND_PCM_STREAM_CAPTURE,0);
    if(r < 0)
    {
        perror("open fail");
        return -1;
    }

    //设置参数
    //初始化pcm属性
    snd_pcm_hw_params_alloca(&params);//params申请内存
    snd_pcm_sw_params_alloca(&swparams);
    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_rate_resample(handle,);
    //设置双声道,小端格式,16位
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(handle, params, 2);

    //设置采样频率
    int rate = 44100;
    snd_pcm_hw_params_set_rate_near(handle,params,&rate,0);

    //设置好的参数回写设备
    r = snd_pcm_hw_params(handle, params);
    if(r < 0)
    {
        perror("snd pcm params fail");
        return -1;
    }
    unsigned buffer_time=0;
    unsigned period_time=0;
    snd_pcm_hw_params_get_buffer_time_max(params,&buffer_time,0);//
    if(buffer_time>500000)
        buffer_time=500000;
    if(buffer_time>0)
        period_time=buffer_time/4;

    snd_pcm_hw_params_set_period_time_near(handle,params,&period_time,0);
    //snd_pcm_hw_params_set_buffer_time_near(handle,params,&buffer_time,0);
    
    //定义帧数
    int  frames=32;

    //获取一个周期有多少帧数据
    snd_pcm_hw_params_get_period_size(params,(snd_pcm_uframes_t*)&frames,0);
    snd_pcm_hw_params_get_rate(params,&rate,0);
    printf("frames=%d, rate=%d\n", frames, rate);
    int size = frames*4;
    unsigned char *buffer = malloc(size);

    snd_pcm_sw_params_current(handle,swparams);
    
    //snd_pcm_sw_params_set_avail_min(handle,swparams)
    snd_pcm_sw_params_set_start_threshold(handle,swparams,0);    
    //snd_pcm_sw_params_set_stop_threshold(handle,swparams,frames+1);
    
    //初始化网络
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in seraddr;
    memset(&seraddr, 0, sizeof(seraddr));
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(8888);
    inet_pton(AF_INET,"127.0.0.1",&seraddr.sin_addr);

    //int times=0;
    while(1)
    {
        //录音
        ret = snd_pcm_readi(handle,buffer,frames);
        printf("read_ret=%d  ",ret);
        /*if(ret != frames)
        {
            snd_pcm_prepare(handle);
            continue;
        }*/
        if( ret == -EPIPE )
        {
            snd_pcm_prepare( handle );
            /*数据满了,没有被alsa读走*/
            fprintf (stderr, "<<<<<<<<<<<<<<<<<<< Buffer Overrun >>>>>>>>>>>>>>>>>\n");
            continue;
        }
        else if( ret == -EBADFD )
        {
            fprintf(stderr, "<<<<<<<<<<<<<<<<<<<< readi error -EBADFD >>>>>>>>>>>>>\n");
            continue;
        }
        else if( ret == -ESTRPIPE )
        {
            fprintf(stderr, "<<<<<<<<<<<<<<<<<<<< readi error -ESTRPIPE >>>>>>>>>>>>>\n");
        }

            //udp发送
        ret = sendto(sockfd, buffer, size, 0, (struct sockaddr*)&seraddr, sizeof(seraddr));
        /*if(ret != frames*4)
          {
          break;

          }*/
        printf("send_ret=%d\n",ret);
        bzero(buffer,sizeof(buffer));
    }
    close(sockfd);
    //关闭
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    return 0;
}


我想要达到的结果

写出另一个客户端传输一段音乐,服务器能同时收听到人声和音乐

  • 写回答

1条回答 默认 最新

  • 伍六七0804 2022-10-11 09:47
    关注

    另一个客户端打开声卡采集声音,通过UDP传给服务器,服务器这边接收到音频后和音乐客户端的声音进行混音。混音的方法有很多,最简单的 混音 = (人声 + 音乐)* 2分之根号2
    比如buf1是人声,buf2是音乐 最后服务器这边送给声卡的声音buf就是 buf[i] =(buf1[i] + buf2[i])*sqrt(2)/2; 这样服务器这边播放出来的声音就是人声和音乐同时播放的效果
    混音算法有很多种,如果这种效果不好可以换其他的方法

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 系统已结题 10月21日
  • 已采纳回答 10月13日
  • 创建了问题 10月10日

悬赏问题

  • ¥15 有没有办法利用libusb读取usb设备数据
  • ¥15 为什么openeluer里面按不了python3呢?
  • ¥15 关于#matlab#的问题:训练序列与输入层维度不一样
  • ¥15 关于Ubuntu20.04.3LTS遇到的问题:在安装完CUDA驱动后,电脑会进入卡死的情况,但可以通过键盘按键进入安全重启,但重启完又会进入该情况!
  • ¥15 关于#嵌入式硬件#的问题:树莓派第一天重装配置python和opencv后第二天打开就成这样,瞎捣鼓搞出来文件夹还是没把原来的界面调回来
  • ¥20 Arduino 循迹小车程序电路出错故障求解
  • ¥20 Arduino 循迹小车程序电路出错故障求解
  • ¥15 C++数组中找第二小的数字程序纠错
  • ¥15 wannier复现图像时berry曲率极值点与高对称点严重偏移
  • ¥15 利用决策森林为什么会出现这样·的问题(关键词-情感分析)