qt中采用窄带speex进行网络语音通话实验程序
本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.
环境:
主机:WIN8
开发环境:Qt5 3.1.2
speex版本:1.0.5
说明:
本程序采样频率为8KHz,量化位数为16位,则码率为128kbps。
speex采用窄带压缩,质量10,压缩比率为62/320,则压缩后的码率为24.8kbps。
本测试程序实现网络语音通讯的功能。
源码:
pro文件加载库文件
INCLUDEPATH += C:\work\test\test_audio_record\libspeex1\include LIBS += -LC:\work\test\test_audio_record\libspeex1 -llibspeex
audio_read.h
#ifndef AUDIO_READ_H #define AUDIO_READ_H #include "world.h" class Audio_Read : public QObject { Q_OBJECT public: Audio_Read(); signals: /********************************************************************* * 发送网络帧 *参数:frame:发送的报文 **********************************************************************/ void sig_net_tx_frame(QByteArray frame); public slots: void readMore(); private: QAudioInput* audio_in; // class member. QIODevice *myBuffer_in; //SPEEX相关全局变量 SpeexBits bits_enc; void *Enc_State; short input_frame[SPEEX_FRAME_BYTE / 2]; //speex压缩输入存储区 short input_frame0[SPEEX_FRAME_BYTE / 2]; //speex压缩输入存储区 char cbits[SPEEX_FRAME_BYTE]; //压缩后数据存储区 char buf[SPEEX_FRAME_BYTE]; //读取声卡存储区 }; #endif // AUDIO_READ_H
audio_read.cpp 读取声卡,并压缩传输
#include "audio_read.h" Audio_Read::Audio_Read() { //speex编码初始化 speex_bits_init(&bits_enc); //Enc_State = speex_encoder_init(&speex_wb_mode); Enc_State = speex_encoder_init(&speex_nb_mode); //设置压缩质量 int tmp = SPEEX_QUALITY; speex_encoder_ctl(Enc_State,SPEEX_SET_QUALITY,&tmp); //声卡采样格式 QAudioFormat format; // set up the format you want, eg. format.setSampleRate(8000); format.setChannelCount(1); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); //format.setByteOrder(QAudioFormat::BigEndian); format.setSampleType(QAudioFormat::UnSignedInt); //format.setSampleType(QAudioFormat::SignedInt); QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(format)) { qWarning()<<"default format not supported try to use nearest"; format = info.nearestFormat(format); } audio_in = new QAudioInput(format, this); myBuffer_in = audio_in->start(); connect(myBuffer_in, SIGNAL(readyRead()), SLOT(readMore())); // Records audio for 3000ms qDebug() <<"record begin!" << endl; } void Audio_Read::readMore() { char bytes[800] = {0}; int i = 0; float input_frame1[320]; QByteArray frame; int nbytes = 0; if (!audio_in) return; QByteArray m_buffer(2048,0); qint64 len = audio_in->bytesReady(); qDebug() << "len1 = " << len; qint64 l = myBuffer_in->read(m_buffer.data(), len); qDebug() << "len2 = " << l; if (len > 640) { return; } frame.clear(); //将读取的数据转换成speex识别的格式 // //大端 // for (i = 0;i < 320;i++) // { // input_frame1[i] = m_buffer[2 * i] | ((short)(m_buffer[2 * i + 1]) << 8); // } // //小端 // for (i = 0;i < SPEEX_FRAME_BYTE / 2;i++) // { // input_frame1[i] = m_buffer[2 * i + 1] | ((short)(m_buffer[2 * i]) << 8); // } //大端 short num = 0; for (i = 0;i < 160;i++) { num = (uint8_t)m_buffer[2 * i] | (((uint8_t)m_buffer[2 * i + 1]) << 8); input_frame1[i] = num; //num = m_buffer[2 * i] | ((short)(m_buffer[2 * i + 1]) << 8); //qDebug() << "float in" << num << input_frame1[i]; } //压缩数据 speex_bits_reset(&bits_enc); speex_encode(Enc_State,input_frame1,&bits_enc); nbytes = speex_bits_write(&bits_enc,bytes,800); qDebug() << "nbytes = " << nbytes; frame.append(bytes,nbytes); //大端 for (i = 0;i < 160;i++) { num = (uint8_t)m_buffer[2 * i + 320] | (((uint8_t)m_buffer[2 * i + 1 + 320]) << 8); input_frame1[i] = num; } //压缩数据 speex_bits_reset(&bits_enc); speex_encode(Enc_State,input_frame1,&bits_enc); nbytes = speex_bits_write(&bits_enc,bytes,800); qDebug() << "nbytes = " << nbytes; frame.append(bytes,nbytes); //发送 // frame.append(bytes,nbytes); // frame.clear(); // frame.append(m_buffer.data(),len); if (Server_Ip != QHostAddress("0")) { sig_net_tx_frame(frame); } }
audio_write.h
#ifndef AUDIO_WRITE_H #define AUDIO_WRITE_H #include "world.h" class Audio_Write : public QObject { Q_OBJECT public: Audio_Write(); signals: public slots: void finishedPlaying(QAudio::State state); /********************************************************************* * 网络接收数据包 *参数:data:接收的数据 **********************************************************************/ void slot_net_rx(QByteArray data); void update2(); private: QAudioOutput* audio_out; // class member. QIODevice *myBuffer_out; QByteArray Buffer_Play; //SPEEX相关全局变量 SpeexBits bits_dec; void *Dec_State; short output_frame[SPEEX_FRAME_BYTE / 2]; //speex解压输出存储区 }; #endif // AUDIO_WRITE_H
audio_write.cpp 接收语音数据,并解码播放
#include "audio_write.h"
Audio_Write::Audio_Write()
{
//speex初始化
speex_bits_init(&bits_dec);
//Dec_State = speex_decoder_init(&speex_wb_mode);
Dec_State = speex_decoder_init(&speex_nb_mode);
QAudioFormat format;
// set up the format you want, eg.
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
//format.setByteOrder(QAudioFormat::BigEndian);
format.setSampleType(QAudioFormat::UnSignedInt);
//format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
if (!info.isFormatSupported(format)) {
qWarning()<<"default format not supported try to use nearest";
format = info.nearestFormat(format);
}
audio_out = new QAudioOutput(format, this);
connect(audio_out,SIGNAL(stateChanged(QAudio::State)),SLOT(finishedPlaying(QAudio::State)));
myBuffer_out = audio_out->start();
qDebug() <<"play begin!" << endl;
QTimer *timer2 = new QTimer(this);
connect(timer2, SIGNAL(timeout()), this, SLOT(update2()));
//timer2->start(10 * INTERVAL);
//timer2->start(5);
}
void Audio_Write::finishedPlaying(QAudio::State state)
{
// if(state == QAudio::IdleState) {
// audio_out->stop();
// inputFile.close();
// delete audio_out;
// }
qDebug() << "play end!" << endl;
}
/*********************************************************************
* 网络接收数据包
*参数:data:接收的数据
**********************************************************************/
void Audio_Write::slot_net_rx(QByteArray data)
{
char bytes[800] = {0};
int i = 0;
float output_frame1[320] = {0};
char buf[800] = {0};
//memcpy(bytes,data.data(),data.length());
qDebug() << "lenght!!!!!!!!!!!!!!" << data.length();
memcpy(bytes,data.data(),data.length() / 2);
//解压缩数据106 62
//speex_bits_reset(&bits_dec);
speex_bits_read_from(&bits_dec,bytes,data.length() / 2);
int error = speex_decode(Dec_State,&bits_dec,output_frame1);
//qDebug() << "error1 = !!!!!!!!!!!!!!" << error;
//将解压后数据转换为声卡识别格式
//大端
short num = 0;
for (i = 0;i < 160;i++)
{
num = output_frame1[i];
buf[2 * i] = num;
buf[2 * i + 1] = num >> 8;
//qDebug() << "float out" << num << output_frame1[i];
}
memcpy(bytes,data.data() + data.length() / 2,data.length() / 2);
//解压缩数据
//speex_bits_reset(&bits_dec);
speex_bits_read_from(&bits_dec,bytes,data.length() / 2);
error = speex_decode(Dec_State,&bits_dec,output_frame1);
qDebug() << "error2 = !!!!!!!!!!!!!!" << error;
//将解压后数据转换为声卡识别格式
//大端
for (i = 0;i < 160;i++)
{
num = output_frame1[i];
buf[2 * i + 320] = num;
buf[2 * i + 1 + 320] = num >> 8;
}
// //小端
// for (i = 0;i < SPEEX_FRAME_BYTE / 2;i++)
// {
// buf[2 * i + 1] = (int)(output_frame1[i]) & 0x00ff;
// buf[2 * i] = (int)(output_frame1[i]) >> 8;
// }
//qDebug() << "size!!!" << myBuffer_out->size();
//if (audio_out->state() == QAudio::IdleState)
//{
qDebug() << "播放";
myBuffer_out->write(buf,640);
//Buffer_Play.append(buf,640);
//myBuffer_out->write(data);
// }
// else
// {
// qDebug() << "忙碌";
// }
}
void Audio_Write::update2()
{
char bytes[800] = {0};
int i = 0;
QByteArray frame;
//short input_short[L_FRAME] = {0};
int j = 0;
//检查是否有剩余空间
qDebug() << "aaaaaaaaa222222222222222:" << audio_out->bytesFree()
<< audio_out->periodSize() << Buffer_Play.length();
if (audio_out && audio_out->state() != QAudio::StoppedState) {
int chunks = audio_out->bytesFree()/audio_out->periodSize();
while (chunks)
{
if (Buffer_Play.length() >= audio_out->periodSize())
{
myBuffer_out->write(Buffer_Play.data(),audio_out->periodSize());
Buffer_Play = Buffer_Play.mid(audio_out->periodSize());
}
else
{
myBuffer_out->write(Buffer_Play);
Buffer_Play.clear();
break;
}
--chunks;
}
}
// if (Count * L_FRAME_COMPRESSED * INTERVAL > file_all.length())
// {
// return;
// }
// //发送
// frame.append(file_all.data() + Count * L_FRAME_COMPRESSED * INTERVAL,L_FRAME_COMPRESSED * INTERVAL);
// Count++;
// slot_net_rx(frame);
}