C++ 在Raspberry Pi模拟输出上接收和播放原始PCM的ALSA配置
我已经使用PulseAudio和Qt5.4的QAudioOutput在Raspberry Pi上的3.5毫米耳机插孔模拟输出上成功播放了音频。音频通过XBee链路从远程麦克风以8KHz、8位采样成功传输 PulseAudio有一个巨大的延迟,所以我决定链接libasound(ALSA),直接播放音频。我的代码在下面,成功打开并播放声音,但几乎无法识别,有很多噼啪声和吱吱声。如果我对着遥控麦克风说话,我会很快听到来自Pi的耳机中的刮擦声和吱吱声(但这不是很好的音频)。我想我的参数搞乱了 1.)数据以BigEndian格式传输-QAudioOutput允许您通知它样本是BigEndian格式的。但这些是U8样本,所以我需要担心endianness吗? 2.)您能看到我下面的配置有什么问题吗? 3.)如何计算Pi上输出的ALSA的片段大小? 4.)有人能解释一下我应该如何将缓冲区写入音频设备吗 谢谢 这是我的密码:C++ 在Raspberry Pi模拟输出上接收和播放原始PCM的ALSA配置,c++,linux,audio,raspberry-pi,alsa,C++,Linux,Audio,Raspberry Pi,Alsa,我已经使用PulseAudio和Qt5.4的QAudioOutput在Raspberry Pi上的3.5毫米耳机插孔模拟输出上成功播放了音频。音频通过XBee链路从远程麦克风以8KHz、8位采样成功传输 PulseAudio有一个巨大的延迟,所以我决定链接libasound(ALSA),直接播放音频。我的代码在下面,成功打开并播放声音,但几乎无法识别,有很多噼啪声和吱吱声。如果我对着遥控麦克风说话,我会很快听到来自Pi的耳机中的刮擦声和吱吱声(但这不是很好的音频)。我想我的参数搞乱了 1.)数据
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++;
}
udpreciver::udpreciver(QObject*父对象):
QObject(父对象)
{
//调试
qDebug()绑定(QHostAddress::Any,0x2616);
如果(!didBind){
qDebug()标称音频缓冲区大小){
lenToRead=标称音频缓冲区大小;
}
//从UDP端口读取数据
m_Socket->readDatagram(buffer.data(),lenToRead,
&发送方和发送方端口);
//启动音频播放
如果(m_NumberUppacketsReceived==0){
qDebug()
没有
snd_pcm_hw_params_set_rate_near()
会将速率更改为最接近的支持速率。您可能不希望这样
ALSA没有碎片大小
它有缓冲区和周期大小;您需要根据您的计时要求设置它们(请参阅)
您不能简单地输出接收到的样本;必须将它们重新采样到播放设备的速度(即使使用相同的标称采样率,也不完全相同)
能否请您对我将如何对UDP接收的样本进行重新采样发表更多意见?我是否可以将ALSA设置为我的配置,然后在每次从UDP接收更多音频数据时写入其缓冲区?谢谢。发送方和接收方的时钟将以稍有不同的速率运行。