2015-02-07 93 views
1

我已经使用PulseAudio和Qt5.4的QAudioOutput在Raspberry Pi上的3.5mm耳机插孔模拟输出上成功播放了音频。音频通过8位采样率的8KHz的XBee链路从远端麦克风成功传输。配置ALSA接收和播放Raspberry Pi上的原始PCM模拟输出

PulseAudio有一个巨大的延迟,所以我决定链接到libasound(ALSA)并直接播放音频。我的代码在下面,并成功打开并播放声音,但几乎无法辨认,有很多crack啪声和吱吱声。如果我对着远端麦克风说话,我很快就会听到从Pi发出的高音划伤和吱吱声(但它不是很好的音频)。我想我有我的参数搞砸了。

1.)数据以BigEndian传输 - QAudioOutput允许您通知它样本是BigEndian。但这些是U8样本,我需要担心排序吗? 2.)你能看到下面我的配置有什么问题吗? 3.)如何找出Pi上输出的ALSA的片段大小? 4.)有人可以解释我应该如何将我的缓冲区写入音频设备?

谢谢!

这里是我的代码:

UdpReceiver::UdpReceiver(QObject *parent) : 
    QObject(parent) 
{ 

    // Debug 
    qDebug() << "Setting up a UDP Socket..."; 

    // Create a socket 
    m_Socket = new QUdpSocket(this); 

    // Bind to the 2616 port 
    bool didBind = m_Socket->bind(QHostAddress::Any, 0x2616); 
    if (!didBind) { 
     qDebug() << "Error - could not bind to UDP Port!"; 
    } 
    else { 
     qDebug() << "Success binding to port 0x2616!"; 
    } 

    // Get notified that data is incoming to the socket 
    connect(m_Socket, SIGNAL(readyRead()), this, SLOT(readyRead())); 

    // Init to Zero 
    m_NumberUDPPacketsReceived = 0; 

} 

void UdpReceiver::readyRead() { 

    // When data comes in 
    QByteArray buffer; 
    buffer.resize(m_Socket->pendingDatagramSize()); 

    QHostAddress sender; 
    quint16 senderPort; 

    // Cap buffer size 
    int lenToRead = buffer.size(); 
    if (buffer.size() > NOMINAL_AUDIO_BUFFER_SIZE) { 
     lenToRead = NOMINAL_AUDIO_BUFFER_SIZE; 
    } 

    // Read the data from the UDP Port 
    m_Socket->readDatagram(buffer.data(), lenToRead, 
         &sender, &senderPort); 

    // Kick off audio playback 
    if (m_NumberUDPPacketsReceived == 0) { 

     qDebug() << "Received Data - Setting up ALSA Now...."; 

     // Error handling 
     int err; 

     // Device to Write to 
     char *snd_device_out = "hw:0,0"; 

     if ((err = snd_pcm_open (&playback_handle, snd_device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 
      fprintf (stderr, "cannot open audio device %s (%s)\n", 
        snd_device_out, 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { 
      fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) { 
      fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { 
      fprintf (stderr, "cannot set access type (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_U8)) < 0) { // Unsigned 8 bit 
      fprintf (stderr, "cannot set sample format (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     uint sample_rate = 8000; 
     if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &sample_rate, 0)) < 0) { // 8 KHz 
      fprintf (stderr, "cannot set sample rate (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 1)) < 0) { // 1 Channel Mono 
      fprintf (stderr, "cannot set channel count (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) { 
      fprintf (stderr, "cannot set parameters (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     snd_pcm_hw_params_free (hw_params); 

     // Flush handle prepare for playback 
     snd_pcm_drop(playback_handle); 

     if ((err = snd_pcm_prepare (playback_handle)) < 0) { 
      fprintf (stderr, "cannot prepare audio interface for use (%s)\n", 
        snd_strerror (err)); 
      exit (1); 
     } 

     qDebug() << "Done Setting up ALSA...."; 

    } 

    // Grab the buffer 
    m_Buffer = buffer.data(); 

    // Write the data to the ALSA device 
    int error; 
    for (int i = 0; i < 10; ++i) { 
     if ((error = snd_pcm_writei (playback_handle, m_Buffer, NOMINAL_AUDIO_BUFFER_SIZE)) != NOMINAL_AUDIO_BUFFER_SIZE) { 
      fprintf (stderr, "write to audio interface failed (%s)\n", 
        snd_strerror (error)); 
      exit (1); 
     } 
    } 

    // Count up 
    m_NumberUDPPacketsReceived++; 

} 

回答

2
  1. snd_pcm_hw_params_set_rate_near()将改变率最接近的支持率。你可能不希望这样。
  2. ALSA没有片段大小。

    它具有缓冲区和周期大小;您需要根据您的时间要求进行设置(请参阅ALSA: Relation between period size of speaker and mic)。

  3. 你不能简单地输出收到的样本;您必须将它们重新采样到播放设备的速度(即使它使用相同的标称采样率,也不完全相同)。
+0

请问您如何评论我如何重新取样我收到的UDP样本?我能不能只将ALSA设置为我的配置,然后在每次从UDP接收更多音频数据时写入它的缓冲区?谢谢。 – PhilBot 2015-02-08 23:31:58

+0

发送者和接收者的时钟将以稍微不同的速率运行。 – 2015-02-09 07:33:29