linux下 pulseaudio录音和播放

1.安装pulseaudio

sudo apt-get install pulseaudio
sudo apt-get install libpulse-dev#开发包
sudo apt-get update
sudo apt-get install ffmpeg		#ffmpeg

2.列出所有的音频输入源,其中包括麦克风 Name 即是pa_simple_new函数dev参数

pactl list sources
#结果
Source #0
        State: RUNNING
        Name: alsa_output.pci-0000_02_02.0.analog-stereo.monitor	#录取系统声音的dev
        Description: Monitor of Internal Audio Analog Stereo
        Driver: module-alsa-card.c
...
Source #1
        State: RUNNING
        Name: alsa_input.pci-0000_02_02.0.analog-stereo	
        Description: Yeti Stereo Microphone Analog Stereo
        Driver: module-alsa-card.c
...

1.发送方 测试代码

//pulse_simple_sender.cpp
#include <iostream>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/pulseaudio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <vector>
#include <thread>
#include <mutex>
#define BUFSIZE 1400
#define PORT 12345

std::vector<std::string> vct_buff;

std::mutex g_mutex;

pa_source_info souceInfo[10];

int indexCount = 0;

bool isNullTerminated1(const char* str) {
    while (*str != '\0') {
        ++str;
    }
    return (*str == '\0');
}

bool isNullTerminated(const char* str,int size) {
    for (int i = 0; i < size; i++) {
        if (str[i] != '\0') {
            return false;
        }
    }
    return true;
}

void process_audio_data(const void *input_data, size_t num_samples) {
    const int16_t *samples = (const int16_t *)input_data;

    for (size_t i = 0; i < num_samples; ++i) {
        std::cout << "samples: " << samples[i] << std::endl;
    }
}

//列出所有的音频输入源
void source_info_callback(pa_context* c, const pa_source_info* i, int eol, void* userdata) {
    if (i) {
        if(indexCount >= 10) return ;
        int len = strlen(i->name);
  	    souceInfo[indexCount].name = new char[len];
        if(souceInfo[indexCount].name == NULL) return;
        memcpy((void*)souceInfo[indexCount++].name,i->name,len);
    }
}

//获取设备信息cmd 'pactl list sources'
int pactl_list_sources()
{
    // 创建PulseAudio主循环
    pa_mainloop* mainloop = pa_mainloop_new();

    // 创建PulseAudio上下文
    pa_mainloop_api* mainloop_api = pa_mainloop_get_api(mainloop);
    pa_context* context = pa_context_new(mainloop_api, "PulseAudio Example");

    // 连接到PulseAudio服务器
    if (pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
        std::cerr << "Error: Unable to connect to PulseAudio server." << std::endl;
        return EXIT_FAILURE;
    }

    // 等待连接成功
    while (pa_context_get_state(context) != PA_CONTEXT_READY) {
        pa_mainloop_iterate(mainloop, 1, nullptr);
    }

    // 请求音频源信息
    pa_operation* op = pa_context_get_source_info_list(context, source_info_callback, nullptr);
    
    // 等待回调完成
    while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
        pa_mainloop_iterate(mainloop, 1, nullptr);
    }

    // 释放资源
    pa_operation_unref(op);  // 等待操作完成后再释放
    pa_context_disconnect(context);
    pa_context_unref(context);
    pa_mainloop_free(mainloop);

    return EXIT_SUCCESS;
}

//mic声音
static void mic_read()
{
    pa_simple *mic_stream = nullptr;
    int error;
    static const pa_sample_spec ss = {
        .format = PA_SAMPLE_S16LE,
        .rate = 44100,
        .channels = 1
    };

    mic_stream = pa_simple_new(
        nullptr,
        "System Stream",
        PA_STREAM_RECORD,
        NULL,
        "System Stream",
        &ss,
        nullptr,
        nullptr,
        &error
    );

    if (!mic_stream) {
        std::cerr << "Could not create system stream: " << pa_strerror(error) << std::endl;
        return ;
    }
    /*FILE *fp = fopen("mic.raw", "wb");
    if (!fp) {
        fprintf(stderr, "Failed to open output file\n");
        return ;
    }*/
    while (true) {
        // Read system audio data
        char systemBuffer[BUFSIZE];
        if (pa_simple_read(mic_stream, systemBuffer, sizeof(systemBuffer), &error) < 0) {
            std::cerr << "Could not read system data: " << pa_strerror(error) << std::endl;
            //fclose(fp);
            pa_simple_free(mic_stream);
            break;
        }
        //fwrite(systemBuffer, sizeof(char), sizeof(systemBuffer), fp);
        std::string str(systemBuffer,sizeof(systemBuffer));
        std::lock_guard<std::mutex> lockGuard(g_mutex);
        vct_buff.push_back(str);
    }

}

//读取系统声音
static void sys_read()
{
    pa_simple *system_stream = nullptr;
    int error;
    static const pa_sample_spec ss = {
        .format = PA_SAMPLE_S16LE,
        .rate = 44100,
        .channels = 1
    };
   
    std::cout << "sys monitor : " << souceInfo[0].name << std::endl;
    system_stream = pa_simple_new(
        nullptr,
        "System Stream",
        PA_STREAM_RECORD,
        //"alsa_output.pci-0000_02_02.0.analog-stereo.monitor",
        souceInfo[0].name,
        "System Stream",
        &ss,
        nullptr,
        nullptr,
        &error
    );

    if (!system_stream) {
        std::cerr << "Could not create system stream: " << pa_strerror(error) << std::endl;
        return ;
    }

    FILE *fp = fopen("sys.raw", "wb");
    if (!fp) {
        fprintf(stderr, "Failed to open output file\n");
        return ;
    }
    
    while (true) {
        // Read system audio data
        char systemBuffer[BUFSIZE];
        if (pa_simple_read(system_stream, systemBuffer, sizeof(systemBuffer), &error) < 0) {
            std::cerr << "Could not read system data: " << pa_strerror(error) << std::endl;
            //fclose(fp);
            pa_simple_free(system_stream);
            break;
        }

        /*if(isNullTerminated(systemBuffer,BUFSIZE)) 
        {
            continue;
        }*/

        //process_audio_data(systemBuffer, sizeof(systemBuffer));
        std::string str(systemBuffer,sizeof(systemBuffer));
        //std::cout << "str.length() = " << str.length() << std::endl;
        fwrite(systemBuffer, sizeof(char), sizeof(systemBuffer), fp);
        // 写入文件
        //fwrite((void *)str.c_str(), sizeof(char), str.length(), fp2);
        std::lock_guard<std::mutex> lockGuard(g_mutex);
        vct_buff.push_back(str);
    }
}

int main() {
    vct_buff.clear();
    pactl_list_sources();//列出当前系统上可用的音频输入源
    // 创建套接字
    int clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (clientSocket == -1) {
        std::cerr << "Error creating socket" << std::endl;
        return -1;
    }
    // 设置服务器地址信息
    sockaddr_in serverAddress;
    serverAddress.sin_family = AF_INET;
    // 使用localhost作为服务器IP地址
    inet_pton(AF_INET, "127.0.0.1", &serverAddress.sin_addr);
    serverAddress.sin_port = htons(PORT);
    
    std::thread t(sys_read);    //读取系统声音
    t.detach();
    std::thread t_mic(mic_read);    //mic声音
    t_mic.detach();
    //std::thread(mic_read,mic_stream).detach();
    while (true) {
        std::vector<std::string> tmp_vec;
        tmp_vec.clear();

        {
            std::lock_guard<std::mutex> lockGuard(g_mutex);
            if(vct_buff.empty()) continue;
            tmp_vec = std::move(vct_buff);
        }
        
        for(auto str : tmp_vec)
        {
            const char *systemBuffer = str.c_str();
            if(sendto(clientSocket, systemBuffer, BUFSIZE, 0, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0)
            {
                perror("Sendto failed");
                break;
            }
            // else{
            //     std::cout << "sendto data success."  << std::endl;
            // }
            usleep(10000);
        }

    }
    for(int i=0;i<indexCount;i++)
    {
        if(souceInfo[0].name)
        {
            delete[] souceInfo[0].name;
        }
    }
    return 0;
}

2.接收方 测试代码

//pulse_simple_receiver.cpp
#include <iostream>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <vector>
#include <thread>
#include <mutex>

#define BUFSIZE 1400
#define PORT 12345

std::vector<std::string> vec_buff;

std::mutex g_playmutex;

static void play_back()
{
    static const pa_sample_spec ss = {
        .format = PA_SAMPLE_S16LE,
        .rate = 44100,
        .channels = 2
    };

    pa_simple *output_stream = nullptr;
    int error;

    output_stream = pa_simple_new(
        nullptr,
        "Output Stream",
        PA_STREAM_PLAYBACK,
        nullptr,  // Use the default device for playback
        "Output Stream",
        &ss,
        nullptr,
        nullptr,
        &error
    );

    if (!output_stream) {
        std::cerr << "Could not create output stream: " << pa_strerror(error) << std::endl;
        return ;
    }
    // 录制音频并写入文件
    /*FILE *fp = fopen("output2.raw", "wb");
    if (!fp) {
        fprintf(stderr, "Failed to open output file\n");
        return ;
    }*/
    while (true)
    {
        std::vector<std::string> tmp_vec;
        tmp_vec.clear();

        {
            std::lock_guard<std::mutex> lockGuard(g_playmutex);
            if(vec_buff.empty()) continue;
            tmp_vec = std::move(vec_buff);
        }
        
        for(auto str : tmp_vec)
        {
            const char *systemBuffer = str.c_str();
            //systemBuffer = str.c_str();
            if (pa_simple_write(output_stream, systemBuffer, BUFSIZE, &error) < 0) {
                std::cerr << "Could not write data to output stream: " << pa_strerror(error) << std::endl;
                pa_simple_free(output_stream);
                break;
            }
            // 写入文件
            fwrite(systemBuffer, sizeof(char), BUFSIZE, fp);
        }
    }
    if(fp)
    {
        fclose(fp);
    }
    return ;
}

int main() {

    std::thread t(play_back);
    t.detach();

    // 创建套接字
    int serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
    if (serverSocket == -1) {
        std::cerr << "Error creating socket" << std::endl;
        return -1;
    }

    // 设置服务器地址信息
    sockaddr_in serverAddress;
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(PORT);

    // 绑定套接字到指定的地址和端口
    if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        std::cerr << "Error binding socket to address" << std::endl;
        close(serverSocket);
        return -1;
    }
    sockaddr_in clientAddress;
    socklen_t clientAddressLength = sizeof(clientAddress);
    char buffer[BUFSIZE];

    // 录制音频并写入文件
    /*FILE *fp1 = fopen("output1.raw", "wb");
    if (!fp1) {
        fprintf(stderr, "Failed to open output file\n");
        return -1;
    }*/

    while (true) {
        // Receive audio data from the client
        ssize_t recv_size = recvfrom(serverSocket, buffer, sizeof(buffer), 0, (struct sockaddr*)&clientAddress, &clientAddressLength);
        if (recv_size < 0) {
            perror("Recvfrom failed");
            break;
        }
        if(recv_size > 0)
        {   
            //fwrite(buffer,sizeof(char),BUFSIZE,fp1);
            std::cout << "receive stream success. " << recv_size << std::endl;
            std::string str(buffer,sizeof(buffer));
            std::lock_guard<std::mutex> lockGuard(g_playmutex);
            vec_buff.push_back(str);
        }
       
    }

    close(serverSocket);
    // if(fp1)
    // {
    //     fclose(fp1);
    // }
    return 0;
}

3.编译

g++ pulse_simple_sender.cpp -o pulse_simple_sender -lpulse-simple -lpulse -lpthread
./pulse_simple_sender
g++ pulse_simple_receiver.cpp -o pulse_simple_receiver -lpulse-simple -lpulse -lpthread
./pulse_simple_receiver

命令行实现录音

parec --format=s16le --rate=44100 --channels=1 --device=your-device | tee output.raw
ffmpeg -f s16le -ar 44100 -ac 1 -i output.raw output.mp3 
CAN长字节DM1报文是指在CAN总线上传输的长度超过8个字节的DM1报文。根据引用\[1\],当要传输的数据长度超过8个字节时,首先使用TPCM进行广播,广播内容包含即将传输报文的PGN、总的数据包长度等信息,然后使用TP.DT进行数据传输。相邻两个TP.DT之间的时间间隔是50ms到200ms。根据引用\[2\],当字节数大于8时,将会使用多帧传输参数组。根据引用\[3\],DM1报文是Diagnostic Message 1, Active Diagnostic Trouble Codes的缩写,用于点亮故障指示灯、红色停机灯等,并周期性播报控制器中处于激活状态的故障码。DM1报文的格式包括各个字节的定义,如故障指示灯、红色停机灯、琥珀色警告指示灯等。因此,CAN长字节DM1报文是指在CAN总线上传输的长度超过8个字节的DM1报文,用于传输更多的故障码信息。 #### 引用[.reference_title] - *1* [车载通信——J1939 DM1](https://blog.csdn.net/weixin_64064747/article/details/130193432)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [J1939广播DM1报文](https://blog.csdn.net/mengdeguodu_/article/details/108173263)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [J1939商用车在线诊断DM1报文](https://blog.csdn.net/traveller93/article/details/120735912)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值