1
0
mirror of https://gitee.com/drabel/LibQQt.git synced 2025-01-04 10:18:44 +08:00

add wavmanager module.

add libqwav
add voicetest2
This commit is contained in:
tianduanrui 2019-07-09 19:50:48 +08:00
parent 8a06f4c946
commit 8c774b9ded
27 changed files with 2194 additions and 0 deletions

View File

@ -0,0 +1,202 @@
#include "libqwav.h"
#include <QFile>
#include <QDebug>
//header data tail
bool addWavHeader ( QIODevice& outputDevice, const QAudioFormat& format )
{
// 开始设置WAV的文件头
// 这里具体的数据代表什么含义请看上一篇文章Qt之WAV文件解析中对wav文件头的介绍
WAVFILEHEADER WavFileHeader;
qstrcpy ( WavFileHeader.RiffName, "RIFF" );
qstrcpy ( WavFileHeader.WavName, "WAVE" );
qstrcpy ( WavFileHeader.FmtName, "fmt " );
qstrcpy ( WavFileHeader.DATANAME, "data" );
// 表示 FMT块 的长度
WavFileHeader.nFmtLength = 16;
// 表示 按照PCM 编码;
WavFileHeader.nAudioFormat = 1;
// 声道数目;
WavFileHeader.nChannleNumber = format.channelCount();
// 采样频率;
WavFileHeader.nSampleRate = format.sampleRate();
//这个地方int到unsinged int数据没有丢失。
//pline() << WavFileHeader.nSampleRate << mFormat.sampleRate();
// 每次采样得到的样本数据位数;
WavFileHeader.nBitsPerSample = format.sampleSize();
// nBytesPerSample 和 nBytesPerSecond这两个值通过设置的参数计算得到;
// 数据块对齐单位(每个采样需要的字节数 = 通道数 × 每次采样得到的样本数据位数 / 8 )
WavFileHeader.nBlockAlign = WavFileHeader.nChannleNumber * WavFileHeader.nBitsPerSample / 8;
// 波形数据传输速率
// (每秒平均字节数 = 采样频率 × 通道数 × 每次采样得到的样本数据位数 / 8 = 采样频率 × 每个采样需要的字节数 )
WavFileHeader.nBytesPerSecond = WavFileHeader.nSampleRate * WavFileHeader.nChannleNumber *
WavFileHeader.nBitsPerSample / 8;
outputDevice.seek ( 0 );
//riff 8 + 4
WavFileHeader.nRiffLength = outputDevice.size() - 8;
outputDevice.write ( WavFileHeader.RiffName, sizeof ( WavFileHeader.RiffName ) - 1 );
outputDevice.write ( ( char* ) &WavFileHeader.nRiffLength, sizeof ( WavFileHeader.nRiffLength ) );
outputDevice.write ( WavFileHeader.WavName, sizeof ( WavFileHeader.WavName ) - 1 );
//fmt 8 + 16
outputDevice.write ( WavFileHeader.FmtName, 4 );
outputDevice.write ( ( char* ) &WavFileHeader.nFmtLength, 4 );
outputDevice.write ( ( char* ) &WavFileHeader.nAudioFormat, sizeof ( WavFileHeader.nAudioFormat ) );
outputDevice.write ( ( char* ) &WavFileHeader.nChannleNumber, sizeof ( WavFileHeader.nChannleNumber ) );
outputDevice.write ( ( char* ) &WavFileHeader.nSampleRate, sizeof ( WavFileHeader.nSampleRate ) );
outputDevice.write ( ( char* ) &WavFileHeader.nBytesPerSecond, sizeof ( WavFileHeader.nBytesPerSecond ) );
outputDevice.write ( ( char* ) &WavFileHeader.nBlockAlign, sizeof ( WavFileHeader.nBlockAlign ) );
outputDevice.write ( ( char* ) &WavFileHeader.nBitsPerSample, sizeof ( WavFileHeader.nBitsPerSample ) );
//data 8
outputDevice.write ( WavFileHeader.DATANAME, 4 );
outputDevice.write ( ( char* ) &WavFileHeader.nDataLength, 4 );
WavFileHeader.fileHeaderSize = outputDevice.pos();
//44
//int headerSize = WavFileHeader.fileHeaderSize;
//qDebug() << headerSize;
return true;
}
bool addWavTail ( QIODevice& outputDevice )
{
QByteArray tailBytes;
tailBytes += "q\xFCq\xFC";
tailBytes += "a\xFB";
tailBytes += "a\xFB";
//8
//pline() << tailBytes << tailBytes.size();
outputDevice.write ( tailBytes );
return true;
}
bool addWavDataBytes ( QIODevice& outputDevice, const QByteArray& bytes )
{
outputDevice.write ( bytes );
return true;
}
bool wavParse ( QString fileName, WAVFILEHEADER& WavFileHeader )
{
QFile fileInfo ( fileName );
if ( !fileInfo.open ( QIODevice::ReadOnly ) )
{
return false;
}
// 警告每一次读都不seek的用户调用了seek不会引发pos变化read会。
// 读取 资源交换文件标志 "RIFF";
fileInfo.read ( WavFileHeader.RiffName, sizeof ( WavFileHeader.RiffName ) - 1 );
// 读取 RIFF 头后字节数;
fileInfo.read ( ( char* ) &WavFileHeader.nRiffLength, sizeof ( WavFileHeader.nRiffLength ) );
// 读取 波形文件标识符 "WAVE";
fileInfo.read ( WavFileHeader.WavName, sizeof ( WavFileHeader.WavName ) - 1 );
// 警告每一次读都不seek的用户调用了seek不会引发pos变化read会。
while ( 1 )
{
char tempName[5] = {0};
unsigned int tempSize = 0;
fileInfo.read ( tempName, 4 );
fileInfo.read ( ( char* ) &tempSize, 4 );
QString strTempName ( tempName );
if ( 0 == strTempName.compare ( "fmt ", Qt::CaseInsensitive ) )
{
strcpy ( WavFileHeader.FmtName, tempName );
WavFileHeader.nFmtLength = tempSize;
// 读取 格式种类;
fileInfo.read ( ( char* ) &WavFileHeader.nAudioFormat, sizeof ( WavFileHeader.nAudioFormat ) );
// 读取 音频通道数目;
fileInfo.read ( ( char* ) &WavFileHeader.nChannleNumber, sizeof ( WavFileHeader.nChannleNumber ) );
// 读取 采样频率;
fileInfo.read ( ( char* ) &WavFileHeader.nSampleRate, sizeof ( WavFileHeader.nSampleRate ) );
// 读取 波形数据传输速率;
fileInfo.read ( ( char* ) &WavFileHeader.nBytesPerSecond, sizeof ( WavFileHeader.nBytesPerSecond ) );
// 读取 数据块对齐单位;
fileInfo.read ( ( char* ) &WavFileHeader.nBlockAlign, sizeof ( WavFileHeader.nBlockAlign ) );
// 读取 每次采样得到的样本数据位数值;
fileInfo.read ( ( char* ) &WavFileHeader.nBitsPerSample, sizeof ( WavFileHeader.nBitsPerSample ) );
// 如果有扩展字节
fileInfo.read ( tempSize - 16 );
}
else if ( 0 == strTempName.compare ( "fact", Qt::CaseInsensitive ) )
{
// 存在fact块,读取数据;
strcpy ( WavFileHeader.FactName, tempName );
// fact块长度;
WavFileHeader.nFactLength = tempSize;
// 读取fact块数据;但是不处理
fileInfo.read ( tempSize );
qDebug() << "fact data" << tempName << tempSize;
}
else if ( 0 == strTempName.compare ( "data", Qt::CaseInsensitive ) )
{
strcpy ( WavFileHeader.DATANAME, tempName );
WavFileHeader.nDataLength = tempSize;
// 读取 音频数据大小;
WavFileHeader.fileDataSize = tempSize;
//文件头大小;
WavFileHeader.fileHeaderSize = fileInfo.pos();
// 注意这里的总大小包括结尾8个字节wav文件有8个结尾字节。"q\xFCq\xFC""a\xFB""a\xFB"
// 文件总大小;
WavFileHeader.fileTotalSize = WavFileHeader.nRiffLength + 8;
break;
}
else
{
qDebug() << "unhandled chunk" << tempName << tempSize;
fileInfo.read ( tempSize );
break;
}
}
//测试OK
// pline() << "filesize" << WavFileHeader.fileTotalSize;
// pline() << "fileheadersize" << WavFileHeader.fileHeaderSize;
// pline() << "filedatasize" << WavFileHeader.fileDataSize;
// pline() << "filetailsize" << WavFileHeader.fileTailSize;
// pline() << WavFileHeader.nChannleNumber << WavFileHeader.nSampleRate << WavFileHeader.nBitsPerSample;
fileInfo.close();
}
/*
* 2 Qt
*/
bool anlysisWavFileHeader ( QString fileName, QAudioFormat& format, TWavFileInfo& fileinfo )
{
WAVFILEHEADER WavFileHeader;
bool ret = wavParse ( fileName, WavFileHeader );
format.setChannelCount ( WavFileHeader.nChannleNumber );
format.setSampleRate ( WavFileHeader.nSampleRate );
format.setSampleSize ( WavFileHeader.nBitsPerSample );
format.setByteOrder ( QAudioFormat::LittleEndian );
format.setCodec ( "audio/pcm" );
format.setSampleType ( QAudioFormat::SignedInt );
fileinfo.fileTotalSize = WavFileHeader.fileTotalSize;
fileinfo.fileHeaderSize = WavFileHeader.fileHeaderSize;
fileinfo.fileDataSize = WavFileHeader.fileDataSize;
fileinfo.fileTailSize = WavFileHeader.fileTailSize;
return true;
}

View File

@ -0,0 +1,98 @@
#ifndef LIBQWAV_H
#define LIBQWAV_H
#include "libqwav_global.h"
#include "string.h"
#include <QAudioFormat>
#include <QIODevice>
//dll __imp_ lib no __imp_
//跟这个extern没关系。
//动态导出、导入时就是有__imp_
//静态导出、导入时就是没有__imp_导出的是函数原型。
//这个extern的作用是在导出库源文件是.c文件时使用者必须使用extern "C"包含对应的头文件。
//就这么一个作用。
//#ifdef __cplusplus
//extern "C" {
//#endif
/*
* 2 使QtFile接口Qt资源文件
*/
// wav文件头信息结构
struct WAVFILEHEADER
{
// RIFF 头;
char RiffName[5];//RIFF
int nRiffLength;//fileLen-8
// 数据类型标识符;
char WavName[5];//WAVE
// 格式块中的块头;
char FmtName[5];//fmt
int nFmtLength;
// 格式块中的块数据;
short nAudioFormat;
short nChannleNumber;
int nSampleRate;
int nBytesPerSecond;
short nBlockAlign;
short nBitsPerSample;
// 附加信息(可选),根据 nFmtLength 来判断;
// 扩展域大小;
short nAppendMessage;
//Fact块,可选字段一般当wav文件由某些软件转化而成则包含该Chunk;
char FactName[5];
int nFactLength;
// DATA块数据块中的块头;
char DATANAME[5];
int nDataLength;//fileDataSize
// 以下是附加的一些计算信息;
int fileDataSize; // 文件音频数据大小;
int fileHeaderSize; // 文件头大小;
int fileTailSize; // 文件尾大小; 一般忽略。
int fileTotalSize; // 文件总大小;
// 理论上应该将所有数据初始化,这里只初始化可选的数据;
WAVFILEHEADER() {
fileTailSize = 8;
memset ( RiffName, 0, 5 );
memset ( WavName, 0, 5 );
memset ( FmtName, 0, 5 );
memset ( FactName, 0, 5 );
}
};
//44 固定的
LIBQWAVSHARED_EXPORT bool addWavHeader ( QIODevice& outputDevice, const QAudioFormat& format );
//8 固定的
LIBQWAVSHARED_EXPORT bool addWavTail ( QIODevice& outputDevice );
LIBQWAVSHARED_EXPORT bool addWavDataBytes ( QIODevice& outputDevice, const QByteArray& bytes );
typedef struct _TWavFileInfo
{
int fileDataSize; // 文件音频数据大小;
int fileHeaderSize; // 文件头大小;
int fileTailSize; // 文件尾大小; 一般忽略。
int fileTotalSize; // 文件总大小;
} TWavFileInfo;
LIBQWAVSHARED_EXPORT bool wavParse ( QString fileName, WAVFILEHEADER& WavFileHeader );
LIBQWAVSHARED_EXPORT bool anlysisWavFileHeader ( QString fileName, QAudioFormat& format, TWavFileInfo& fileinfo );
//#ifdef __cplusplus
//}
//#endif
#endif // LIBQWAV_H

View File

@ -0,0 +1,18 @@
#ifndef LIBQWAV_GLOBAL_H
#define LIBQWAV_GLOBAL_H
#include <QtCore/qglobal.h>
#ifdef Q_OS_WIN
#if defined(LIBQWAV_LIBRARY)
# define LIBQWAVSHARED_EXPORT Q_DECL_EXPORT
#elif defined(LIBQWAV_STATIC_LIBRARY)
# define LIBQWAVSHARED_EXPORT
#else
# define LIBQWAVSHARED_EXPORT Q_DECL_IMPORT
#endif
#else
# define LIBQWAVSHARED_EXPORT
#endif
#endif // LIBQWAV_GLOBAL_H

View File

@ -0,0 +1,317 @@
#include "qqtwavaudiomanager.h"
#include "libqwav.h"
QQtWavAudioInput::QQtWavAudioInput ( QObject* parent ) : QObject ( parent )
{
mTimer = new QTimer ( this );
connect ( mTimer, SIGNAL ( timeout() ), SLOT ( slotTimeout() ) );
mFileBytes.clear();
mBytes.clear();
mBytesBuffer.setBuffer ( &mBytes );
mTimerInterval = 20;
}
QIODevice* QQtWavAudioInput::setSourceFile ( const QString& localFile )
{
//如果开着,不管。
mSourceFile = localFile;
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
//判断文件类型是否接受
QMimeDatabase mimedb;
QMimeType mimetype = mimedb.mimeTypeForFile ( mSourceFile );
if ( !QSoundEffect::supportedMimeTypes().contains ( mimetype.name(), Qt::CaseInsensitive ) )
{
pline() << "filename" << localFile << "mimetype" << mimetype.name()
<< QSoundEffect::supportedMimeTypes().contains ( mimetype.name(), Qt::CaseInsensitive ) ;
pline() << "can't play file";
return NULL;
}
#endif
//判断音频具体格式
//支持qrc文件
TWavFileInfo info;
bool ret = anlysisWavFileHeader ( mSourceFile, mFormat, info );
//仅仅支持本地文件
//bool ret = anlysisWavFileHeader_C ( localFile );
if ( !ret )
{
pline() << "wav format parse fail";
return NULL;
}
mFileTotalSize = info.fileTotalSize;
mFileHeaderSize = info.fileHeaderSize;
mFileDataSize = info.fileDataSize;
mFileTailSize = info.fileTailSize;
return &mBytesBuffer;
}
void QQtWavAudioInput::setTimerInterval ( int millSecond ) { mTimerInterval = millSecond; }
void QQtWavAudioInput::start()
{
stop();
mChannelCount = mFormat.channelCount();
mSampleSize = mFormat.sampleSize();
mSampleRate = mFormat.sampleRate();
//读取到数据
QFile f ( mSourceFile );
f.open ( QFile::ReadOnly );
QByteArray b = f.read ( mFileHeaderSize );
//这个地方测试完成内部解析wav头两个都很成功。C Qt 都OK.
//pline() << b;
mFileBytes = f.read ( mFileDataSize );
f.close();
mBytesBuffer.open ( QIODevice::ReadWrite );
mTimer->start ( mTimerInterval );
return;
}
void QQtWavAudioInput::stop()
{
//如果正在播放,先关闭写入音频数据流。
mTimer->stop();
//如果存在,则清空。
//这两个变量Buffer是什么操作关系QBuffer是QByteArray的QIODevice套。
//改变QByteArray会直接影响QBuffer的读写。QBuffer也会影响QByteArray的内容。
mFileBytes.clear();
mBytes.clear();
if ( mBytesBuffer.isOpen() )
mBytesBuffer.close();
}
int QQtWavAudioInput::fileTotalSize() {return mFileTotalSize;}
int QQtWavAudioInput::fileHeaderSize() { return mFileHeaderSize; }
int QQtWavAudioInput::fileDataSize() {return mFileDataSize;}
int QQtWavAudioInput::fileTailSize() {return mFileTailSize;}
void QQtWavAudioInput::slotTimeout()
{
//1s 字节数 = 采样率 * 采样深度(位宽)* 通道数 / 8
//mTimerInterval ms 字节数 = 1s 字节数 / (1000/mTimerInterval)
//每个音符 字节数 = 采样深度(位宽)* 通道数 / 8;
int frameSize = mSampleRate * mSampleSize * mChannelCount / 8 / ( 1000 / mTimerInterval );
QByteArray tempBytes;
if ( mFileBytes.size() > frameSize )
{
tempBytes.resize ( frameSize );
}
else
{
tempBytes.resize ( mFileBytes.size() );
}
//pline() << mFileBytes.size() << tempBytes.size() << frameSize;
//mFileBytes 逐渐减少
mFileBytes >> tempBytes;
//这是给用户的。
mBytes = tempBytes;
//回到初始位置
mBytesBuffer.seek ( 0 );
//激发readyRead信号
mBytesBuffer.write ( 0 );
if ( mFileBytes.isEmpty() )
{
//pline() << mFileBytes.size() << 0 << frameSize;
//这里不要关闭Buffer客户一般还没用完。
mTimer->stop();
}
return;
}
QQtWavAudioOutput::QQtWavAudioOutput ( QObject* parent ) : QObject ( parent )
{
mFileBytes.clear();
mBytesBuffer.setBuffer ( &mFileBytes );
mFormat.setByteOrder ( QAudioFormat::LittleEndian );
mFormat.setChannelCount ( 2 );
mFormat.setCodec ( "audio/pcm" );
mFormat.setSampleRate ( 44100 );
mFormat.setSampleSize ( 16 );
mFormat.setSampleType ( QAudioFormat::SignedInt );
}
QIODevice* QQtWavAudioOutput::setSourceFile ( const QString& localFile )
{
mSourceFile = localFile;
return &mBytesBuffer;
}
QAudioFormat& QQtWavAudioOutput::format() { return mFormat; }
void QQtWavAudioOutput::start()
{
if ( mSourceFile.isEmpty() )
return;
//这里清空文件不会发生保存不会这里清空Buffer。
mFileBytes.clear();
if ( mBytesBuffer.isOpen() )
mBytesBuffer.close();
mBytesBuffer.open ( QIODevice::WriteOnly );
}
void QQtWavAudioOutput::stop()
{
//在stop的时候才会把数据全部存储到wav文件
if ( mSourceFile.isEmpty() )
return;
//防止多次关闭导致音频文件被破坏。
if ( mFileBytes.isEmpty() )
return;
QFile file ( mSourceFile );
file.open ( QFile::Truncate | QFile::WriteOnly );
addWavHeader ( file, mFormat );
file.write ( mFileBytes );
addWavTail ( file );
//这个时候Header里面RiffLength是错误的。改写
file.seek ( 0 );
addWavHeader ( file, mFormat );
//现在纠正好了。
file.close();
mFileBytes.clear();
if ( mBytesBuffer.isOpen() )
mBytesBuffer.close();
}
QQtWavAudioManager::QQtWavAudioManager ( QObject* parent ) : QObject ( parent )
{
mInputManager = new QQtWavAudioInput ( this );
mOutputManager = new QQtWavAudioOutput ( this );
mInputDevice = mInputManager->device();
mOutputDevice = mOutputManager->device();
connect ( mInputDevice, SIGNAL ( readyRead() ),
this, SIGNAL ( readyRead() ) );
}
QQtWavAudioManager::~QQtWavAudioManager()
{
stopInput();
stopOutput();
}
void QQtWavAudioManager::setInputSourceFile ( const QString& localFile )
{
QIODevice* ioDev = mInputManager->setSourceFile ( localFile );
if ( !ioDev )
{
pline() << mInputManager->sourceFile()
<< inputAudioFormat().sampleSize()
<< inputAudioFormat().sampleRate()
<< inputAudioFormat().channelCount()
<< "open failed, errcode:"
<< "-1";
return;
}
}
QString QQtWavAudioManager::inputSourceFile() { return mInputManager->sourceFile(); }
void QQtWavAudioManager::setOutputSourceFile ( const QString& localFile )
{
QIODevice* ioDev = mOutputManager->setSourceFile ( localFile );
if ( !ioDev )
{
pline() << mOutputManager->sourceFile()
<< outputAudioFormat().sampleSize()
<< outputAudioFormat().sampleRate()
<< outputAudioFormat().channelCount()
<< "open failed, errcode:"
<< "-1";
return;
}
}
QString QQtWavAudioManager::outputSourceFile() { return mOutputManager->sourceFile(); }
const QAudioFormat& QQtWavAudioManager::inputAudioFormat() { return mInputManager->format(); }
QAudioFormat& QQtWavAudioManager::outputAudioFormat() { return mOutputManager->format(); }
int QQtWavAudioManager::inputFileTotalSize() { return mInputManager->fileTotalSize(); }
int QQtWavAudioManager::inputFileHeaderSize() { return mInputManager->fileHeaderSize(); }
int QQtWavAudioManager::inputFileDataSize() { return mInputManager->fileDataSize(); }
int QQtWavAudioManager::inputFileTailSize() { return mInputManager->fileTailSize(); }
QQtWavAudioInput* QQtWavAudioManager::inputManager() { return mInputManager; }
QIODevice* QQtWavAudioManager::inputDevice() { return mInputDevice; }
QQtWavAudioOutput* QQtWavAudioManager::outputManager() { return mOutputManager; }
QIODevice* QQtWavAudioManager::outputDevice() { return mOutputDevice; }
void QQtWavAudioManager::startInput()
{
stopInput();
mInputManager->start();
}
void QQtWavAudioManager::stopInput()
{
mInputManager->stop();
}
void QQtWavAudioManager::startOutput()
{
stopOutput();
mOutputManager->start();
}
void QQtWavAudioManager::stopOutput()
{
mOutputManager->stop();
}
QByteArray QQtWavAudioManager::readAll()
{
return mInputDevice->readAll();
}
QByteArray QQtWavAudioManager::read ( qint64 maxlen )
{
return mInputDevice->read ( maxlen );
}
void QQtWavAudioManager::write ( const QByteArray& bytes )
{
mOutputDevice->write ( bytes );
}

View File

@ -0,0 +1,220 @@
#ifndef QQTWAVAUDIOMANAGER_H
#define QQTWAVAUDIOMANAGER_H
#include <QObject>
#include <qqtcore.h>
#include <qqt-local.h>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QMimeType>
#include <QMimeData>
#include <QMimeDatabase>
#include <QSound>
#include <QSoundEffect>
#endif
#include <qqtaudiomanager.h>
//设计思路QQtWavAudioInput和QQtWavAudioOutput两边都是内存和wav文件为内存服务。
/**
* @brief The QQtWavAudioInput class
* QQtWavAudioInput具备QAudioInput的能力wav文件的音频帧readyRead输出
* /10ms一帧QIODevice readyRead发射给用户
* QQtAudioInput里支持的设备比较广泛
*
* SourcefileQIODevice
*
* 使
* Qt QBuffer QByteArray QIODeviceQMemIODevice = QBuffer
* QByteArrayQBuffer封装
* open QIODevice::open(mode)
* wavLibrarylibsndfile
* wavwav的音频格式来设定输出设备wav里选取进行使用使wav的
*
* QAudioDecoderx-wavdefaultServiceProvider::requestService(): no service found for - "org.qt-project.qt.audiodecode"
* QSoundEffect支持那么多wav格式("audio/x-wav", "audio/wav", "audio/wave", "audio/x-pn-wav")
* LibQQt库
* QAudioDecoder保存了一个提交#5643241
* QWavAudioEffect保存了一个提交#5f43622
*/
class QQTSHARED_EXPORT QQtWavAudioInput : public QObject
{
Q_OBJECT
public:
explicit QQtWavAudioInput ( QObject* parent = nullptr );
//自动解析格式,和文件大小
QIODevice* setSourceFile ( const QString& localFile );
QString sourceFile() { return mSourceFile; }
//读取用。这个是个内部公用的,并不是临时的。碰巧了。
QIODevice* device() { return &mBytesBuffer; }
//设置读文件的时钟快慢(硬盘快,时钟快,每次读的少;硬盘慢,时钟慢,每次读的多)
//采样间隔 10-100ms default: macOS SSD 20 ms
//windows 机械硬盘 100ms, windows 机械硬盘 is slower than macOS SSD.
int timerInterval() const { return mTimerInterval; }
void setTimerInterval ( int millSecond = 20 );
//可以频繁开启tip用完一定要关闭系统会自动关闭。
//已经检查测试,没有文件设备漏开关问题。
void start();
void stop();
//每次修改SourceFile这些都会改变。在不改变SourceFile的时候是内部使用的值。
const QAudioFormat& format() { return mFormat; }
int fileTotalSize();
int fileHeaderSize();
int fileDataSize();
int fileTailSize();
signals:
public slots:
private slots:
void slotTimeout();
private:
QString mSourceFile;
//用于保存文件全部Bytes
QByteArray mFileBytes;
//用于和Buffer联系给用户提供每次读取的帧。
QByteArray mBytes;
QBuffer mBytesBuffer;
//每次设置新Source会改变这些值。
QAudioFormat mFormat;
int mFileDataSize;
int mFileHeaderSize;
int mFileTailSize;
int mFileTotalSize;
//这三个不准公开出来啊。
int mSampleRate;
int mSampleSize;
int mChannelCount;
QTimer* mTimer;
int mTimerInterval;
public:
protected:
public:
protected:
};
class QQTSHARED_EXPORT QQtWavAudioOutput : public QObject
{
Q_OBJECT
public:
explicit QQtWavAudioOutput ( QObject* parent = nullptr );
QIODevice* setSourceFile ( const QString& localFile );
QString sourceFile() { return mSourceFile; }
//写入用
QIODevice* device() { return &mBytesBuffer; }
//用户务必设置format默认值为2 16 44100
QAudioFormat& format();
void start();
void stop();
protected:
private:
QString mSourceFile;
QAudioFormat mFormat;
QBuffer mBytesBuffer;
QByteArray mFileBytes;
};
/**
* @brief The QQtWavAudioManager class
* Wav媒体音频管理器
*
* Wav音频管理器目标为用户提供Wav媒体的音频数据帧Wav媒体的功能
*/
class QQTSHARED_EXPORT QQtWavAudioManager : public QObject
{
Q_OBJECT
public:
explicit QQtWavAudioManager ( QObject* parent = nullptr );
~QQtWavAudioManager();
/**
* Wav媒体
*/
//设置输入或者输出wav文件
void setInputSourceFile ( const QString& localFile );
QString inputSourceFile();
void setOutputSourceFile ( const QString& localFile );
QString outputSourceFile();
/**
*
*/
//获取输入文件的音频流格式
const QAudioFormat& inputAudioFormat();
//设置输出音频流的音频格式,输出保存的时候使用。
QAudioFormat& outputAudioFormat();
//每次修改SourceFile这些都会改变。
int inputFileTotalSize();
int inputFileHeaderSize();
int inputFileDataSize();
int inputFileTailSize();
/**
* Wav媒体
*/
QQtWavAudioInput* inputManager();
//如果输入,从这里读取帧
QIODevice* inputDevice();
QQtWavAudioOutput* outputManager();
//如果输出,从这里写入帧,提前设置好格式哦...
QIODevice* outputDevice();
void startInput();
void stopInput();
void startOutput();
void stopOutput();
//这两个是方便函数,一般都用这几个进行读写,不使用上边的。
QByteArray readAll();
QByteArray read ( qint64 maxlen );
void write ( const QByteArray& bytes );
signals:
/*音频数据准备就绪readAll即可读取。*/
void readyRead();
public slots:
private:
QQtWavAudioInput* mInputManager;
QQtWavAudioOutput* mOutputManager;
QIODevice* mInputDevice;
QIODevice* mOutputDevice;
//这三个不准公开出来。
int mSampleRate;
int mSampleSize;
int mChannelCount;
};
#endif // QQTWAVAUDIOMANAGER_H

View File

@ -0,0 +1,163 @@
#include "qqtwavsoundeffect.h"
QQtWavSoundEffect* QQtWavSoundEffect::msInstance = NULL;
QQtWavSoundEffect* QQtWavSoundEffect::Instance ( QObject* parent )
{
if ( !msInstance )
msInstance = new QQtWavSoundEffect ( parent );
return msInstance;
}
QQtWavSoundEffect::QQtWavSoundEffect ( QObject* parent ) : QObject ( parent )
{
mVolume = 1;
mIOInput = NULL;
mLooping = 1;
mLoops = 1;
}
void QQtWavSoundEffect::setOutputDevice ( const QAudioDeviceInfo& output )
{
if ( output.isNull() )
manager.outputDeviceInfo() = QQtAudioManager::defaultOutputDevice();
else
manager.outputDeviceInfo() = output;
}
void QQtWavSoundEffect::useDefaultOutputDevice()
{
manager.outputDeviceInfo() = QQtAudioManager::defaultOutputDevice();
}
void QQtWavSoundEffect::useCustomOutputDevice ( const QAudioDeviceInfo& output )
{
manager.outputDeviceInfo() = output;
}
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
QDebug& operator << ( QDebug& dbg, const QAudioFormat& fmt )
{
return dbg;
}
#endif
void QQtWavSoundEffect::play ( QString localFile )
{
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
//判断文件类型是否接受
QMimeDatabase mimedb;
QMimeType mimetype = mimedb.mimeTypeForFile ( localFile );
if ( !QSoundEffect::supportedMimeTypes().contains ( mimetype.name(), Qt::CaseInsensitive ) )
{
pline() << "filename" << localFile << "mimetype" << mimetype.name()
<< QSoundEffect::supportedMimeTypes().contains ( mimetype.name(), Qt::CaseInsensitive ) ;
pline() << "can't play file";
return;
}
#endif
mSourceFile = localFile;
stop();
mIOInput = mWavInput.setSourceFile ( localFile );
connect ( mIOInput, SIGNAL ( readyRead() ),
this, SLOT ( readyRead() ) );
QAudioDeviceInfo& usingOutput = manager.outputDeviceInfo();
QAudioFormat fmt = mWavInput.format();
//输出设备是否支持格式是否支持
if ( !usingOutput.isFormatSupported ( fmt ) )
{
//当前使用设备是否支持
pline() << "output cant support" << fmt;
fmt = usingOutput.nearestFormat ( fmt ); //转换为最接近格式
pline() << "use format" << fmt;
}
manager.outputAudioFormat() = fmt;
manager.startOutput();
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
//默认是静音的。
manager.outputManager()->setVolume ( mVolume );
#endif
//不响,音频输出设备接受顺序的间隔的输出,不接受一股脑输出。
//manager.write ( bytes );
//OK, 达到QSound效果。
mWavInput.start();
}
void QQtWavSoundEffect::stop()
{
//如果正在播放,先关闭
if ( mIOInput )
{
mWavInput.stop();
manager.stopOutput();
disconnect ( mIOInput, SIGNAL ( readyRead() ),
this, SLOT ( readyRead() ) );
mIOInput = NULL;
}
mLooping = 1;
mDataSize = 0;
}
void QQtWavSoundEffect::setVolume ( qreal volume )
{
mVolume = volume;
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
manager.outputManager()->setVolume ( mVolume );
#endif
}
int QQtWavSoundEffect::loops() const { return mLoops; }
int QQtWavSoundEffect::loopsRemaining() const
{
return mLoops - mLooping;
}
void QQtWavSoundEffect::setLoops ( int loops )
{
mLoops = loops;
}
void QQtWavSoundEffect::readyRead()
{
QByteArray bytes = mIOInput->readAll();
//pline() << bytes.size();
manager.write ( bytes );
mDataSize += bytes.size();
if ( mDataSize == mWavInput.fileDataSize() )
{
int loop = mLooping;
if ( loop < loops() )
play ( mSourceFile );
mLooping = loop + 1;
}
}
QQtWavSoundEffect* QQtWavSound ( QString localFile )
{
if ( !localFile.isEmpty() )
QQtWavSoundEffect::Instance ( )->play ( localFile );
return QQtWavSoundEffect::Instance();
}

View File

@ -0,0 +1,80 @@
#ifndef QQTAUDIOEFFECT_H
#define QQTAUDIOEFFECT_H
#include <qqtwavaudiomanager.h>
#include <qqtcore.h>
#include <qqt-local.h>
/*
* QQtWavSoundEffect = QSoundEffect + QSound +...
* QQtWavSoundEffect支持从wav获取声音并输出QSoundEffect+QSound
* QQtWavSoundEffect默认使用默认输出设备
*/
class QQTSHARED_EXPORT QQtWavSoundEffect : public QObject
{
Q_OBJECT
public:
//这样做的目的,在于使用一个实例去播放音效。
//如果是个临时变量,函数执行返回了,但是还没播放完,甚至还没来得及开始呢,这时,播放不出来的。
static QQtWavSoundEffect* Instance ( QObject* parent = nullptr );
explicit QQtWavSoundEffect ( QObject* parent = nullptr );
~QQtWavSoundEffect() {
stop();
}
//设置设备以后,不需要每次都设置
//更换设备不会引发播放更改只会更改内部设备记录。调用play才会导致播放更改。
void setOutputDevice ( const QAudioDeviceInfo& output = QAudioDeviceInfo() );
void useDefaultOutputDevice();
void useCustomOutputDevice ( const QAudioDeviceInfo& output );
void play ( QString localFile );
void stop();
//设置声音以后,不需要每次都要设置。
void setVolume ( qreal volume );
//设置loop会保存下来不需要每次设置。
int loops() const;
int loopsRemaining() const;
void setLoops ( int loops );
//设置读文件的时钟快慢(硬盘快,时钟快,每次读的少;硬盘慢,时钟慢,每次读的多)
//默认使用 macOS SSD 20ms
//Windows上机械硬盘可能会延迟可以设置100ms。
void setTimerInterval ( int millSecond = 20 ) {
mWavInput.setTimerInterval ( millSecond );
}
private slots:
void readyRead();
private:
//不需要额外初始化的地方
//mingw32 5.3 静态成员不准导出?作为静态类这块编译出现错误
//error: definition of static data member 'QQtWavSoundEffect::msInstance' of dllimport'd class
//这个已经查出来了在qqt_header.pri有一个WIN64的宏缺失导致QQT_STATIC_LIBRARY缺失引发QQt在QQTSHAREDEXPORT=import下编译所以引发这个变量重新定义的错误而报错是在导入的类里面定义了静态成员也就是说导入类不准许静态成员的定义初始化代码出现引入类的静态成员在自己的实现文件里出现了定义。变量重定义了。
static QQtWavSoundEffect* msInstance;
QQtWavAudioInput mWavInput;
QIODevice* mIOInput;
QQtAudioManager manager;
QString mSourceFile;
int mDataSize;
//volume会被记住。
qreal mVolume;
int mLoops;
int mLooping;
};
//在使用QQtWavSound等函数之前调用类的instance函数+parent 初始化一下实例。
//=QSound::play()
QQTSHARED_EXPORT QQtWavSoundEffect* QQtWavSound ( QString localFile = "" );
#endif // QQTAUDIOEFFECT_H

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,99 @@
<?xml version="1.0"?>
<manifest package="org.qtproject.example.voicetest2" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="voicetest2" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="声音设备2" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="voicetest2"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="full"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- %%INSERT_FEATURES -->
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PRIVILEGED"/>
<uses-permission android:name="android.permission.FORCE_BACK"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_INPUT_STATE"/>
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
<uses-permission android:name="android.permission.DEVICE_POWER"/>
<uses-permission android:name="android.permission.SET_DEBUG_APP"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SET_ORIENTATION"/>
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY"/>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

18
test/voicetest2/main.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "mainwindow.h"
#include <QQtApplication>
#include <qqtwidgets.h>
#include <qqtapplication.h>
int main ( int argc, char* argv[] )
{
QQtApplication a ( argc, argv );
MainWindow w;
w.show();
#ifdef __ANDROID__
w.showMaximized();
#endif
return a.exec();
}

View File

@ -0,0 +1,561 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qqtaudiomanager.h>
#include <qqtcore.h>
#include <qsound.h>
#include <qsoundeffect.h>
#include <qqtsoundeffect.h>
#include <qqtframe.h>
MainWindow::MainWindow ( QWidget* parent ) :
QMainWindow ( parent ),
ui ( new Ui::MainWindow )
{
ui->setupUi ( this );
connect ( &manager, SIGNAL ( readyRead() ), this, SLOT ( readyRead() ) );
connect ( &wavRecManager, SIGNAL ( readyRead() ), this, SLOT ( wavRecReadyRead() ) );
connect ( &wavFileManager, SIGNAL ( readyRead() ), this, SLOT ( wavFileReadyRead() ) );
QList<QAudioDeviceInfo> ladInfo;
ladInfo = QAudioDeviceInfo::availableDevices ( QAudio::AudioInput );
QListIterator<QAudioDeviceInfo> itor ( ladInfo );
pline() << "本机音频输入设备列表";
while ( itor.hasNext() )
pline() << itor.next().deviceName();
pline() << "默认输入设备" << QAudioDeviceInfo::defaultInputDevice().deviceName();
pline() << "输入设备详细信息";
itor.toFront();
while ( itor.hasNext() )
{
QAudioDeviceInfo adInfo = itor.next();
pline() << adInfo.deviceName();
pline() << adInfo.supportedByteOrders();
pline() << adInfo.supportedChannelCounts();
pline() << adInfo.supportedCodecs();
pline() << adInfo.supportedSampleRates();
pline() << adInfo.supportedSampleSizes();
pline() << adInfo.supportedSampleTypes();
}
QList<QAudioDeviceInfo> ladOutputInfo;
ladOutputInfo = QAudioDeviceInfo::availableDevices ( QAudio::AudioOutput );
QListIterator<QAudioDeviceInfo> itor2 ( ladOutputInfo );
pline() << "本机音频输出设备列表";
while ( itor2.hasNext() )
pline() << itor2.next().deviceName();
pline() << "默认输出设备" << QAudioDeviceInfo::defaultOutputDevice().deviceName();
pline() << "输出设备详细信息";
itor2.toFront();
while ( itor2.hasNext() )
{
QAudioDeviceInfo adInfo = itor2.next();
pline() << adInfo.deviceName();
pline() << adInfo.supportedByteOrders();
pline() << adInfo.supportedChannelCounts();
pline() << adInfo.supportedCodecs();
pline() << adInfo.supportedSampleRates();
pline() << adInfo.supportedSampleSizes();
pline() << adInfo.supportedSampleTypes();
}
pline() << "..........................";
connect ( ui->inputListWidget->selectionModel(), SIGNAL ( currentRowChanged ( QModelIndex, QModelIndex ) ),
this, SLOT ( currentInputRowChanged ( QModelIndex, QModelIndex ) ) );
connect ( ui->outputListWidget->selectionModel(), SIGNAL ( currentRowChanged ( QModelIndex, QModelIndex ) ),
this, SLOT ( currentOutputRowChanged ( QModelIndex, QModelIndex ) ) );
// on_pushButton_2_clicked();
// ui->inputListWidget->setCurrentRow ( 0 );
// ui->outputListWidget->setCurrentRow ( 0 );
// ui->inputListWidget->setFocus();
ui->inHS->setRange ( 0, 100 );
ui->inHS->setValue ( 100 );
ui->outHS->setRange ( 0, 100 );
ui->outHS->setValue ( 100 );
//pline() << QSoundEffect::supportedMimeTypes();
QSoundEffect e;
e.setLoopCount ( 1 );
e.setVolume ( 0.9f );
e.setMuted ( false );
//不响
QUrl u;
u.setUrl ( "http://xmdx.sc.chinaz.com/Files/DownLoad/sound1/201802/9733.wav" );
e.setSource ( u );
//e.play();
//响
e.setSource ( QUrl::fromLocalFile ( conf_res ( "9733.wav" ) ) );
//e.play();
//响
//QSound::play ( conf_res ( "9733.wav" ) );
//响
QQtWavSoundEffect e1;
//e1.play ( conf_res ( "9733.wav" ) );
//响
//QApplication::beep();
//建议初始化
QQtWavSoundEffect::Instance ( this );
//可以多次循环调用。
QQtWavSound ( conf_res ( "9733.wav" ) );
//QQtSleep ( 3000 );
}
MainWindow::~MainWindow()
{
delete ui;
}
QAudioDeviceInfo MainWindow::findInputAudioDeviceInfoByName ( QString devName )
{
QList<QAudioDeviceInfo> ladInfo;
ladInfo = QQtAudioManager::availableInputDevices();
QListIterator<QAudioDeviceInfo> itor ( ladInfo );
while ( itor.hasNext() )
{
QAudioDeviceInfo adInfo = itor.next();
if ( devName == adInfo.deviceName() )
return adInfo;
}
}
QAudioDeviceInfo MainWindow::findOutputAudioDeviceInfoByName ( QString devName )
{
QList<QAudioDeviceInfo> ladInfo;
ladInfo = QQtAudioManager::availableOutputDevices();
QListIterator<QAudioDeviceInfo> itor ( ladInfo );
while ( itor.hasNext() )
{
QAudioDeviceInfo adInfo = itor.next();
if ( devName == adInfo.deviceName() )
return adInfo;
}
}
void MainWindow::on_pushButton_2_clicked()
{
ui->inputListWidget->clear();
QList<QAudioDeviceInfo> ladInfo;
ladInfo = QQtAudioManager::availableInputDevices();
QListIterator<QAudioDeviceInfo> itor ( ladInfo );
pline() << "本机音频输入设备列表";
while ( itor.hasNext() )
{
QAudioDeviceInfo adInfo = itor.next();
pline() << adInfo.deviceName();
ui->inputListWidget->addItem ( adInfo.deviceName() );
}
ui->outputListWidget->clear();
QList<QAudioDeviceInfo> ladOutputInfo;
ladOutputInfo = QQtAudioManager::availableOutputDevices();
QListIterator<QAudioDeviceInfo> itor2 ( ladOutputInfo );
pline() << "本机音频输出设备列表";
while ( itor2.hasNext() )
{
QAudioDeviceInfo adInfo = itor2.next();
pline() << adInfo.deviceName();
ui->outputListWidget->addItem ( adInfo.deviceName() );
}
}
void MainWindow::currentInputRowChanged ( QModelIndex cur, QModelIndex )
{
/*在清空设备列表时clear函数会调用多次这个函数。在这里用cur valid加以过滤否则程序会崩溃退出。*/
if ( !cur.isValid() )
return;
ui->inBit->clear();
ui->inChn->clear();
ui->intRate->clear();
QString name = cur.data().toString();
QAudioDeviceInfo dev = findInputAudioDeviceInfoByName ( name );
if ( dev.isNull() )
return;
QList<int> size = dev.supportedSampleSizes();
QListIterator<int> itor ( size );
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->inBit->addItem ( s0 );
}
itor = dev.supportedChannelCounts();
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->inChn->addItem ( s0 );
}
itor = dev.supportedSampleRates();
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->intRate->addItem ( s0 );
}
}
void MainWindow::currentOutputRowChanged ( QModelIndex cur, QModelIndex )
{
if ( !cur.isValid() )
return;
ui->outBit->clear();
ui->outChn->clear();
ui->outRate->clear();
QString name = cur.data().toString();
QAudioDeviceInfo dev = findOutputAudioDeviceInfoByName ( name );
if ( dev.isNull() )
return;
QList<int> size = dev.supportedSampleSizes();
QListIterator<int> itor ( size );
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->outBit->addItem ( s0 );
}
itor = dev.supportedChannelCounts();
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->outChn->addItem ( s0 );
}
itor = dev.supportedSampleRates();
while ( itor.hasNext() )
{
QString s0 = QString::number ( itor.next() );
ui->outRate->addItem ( s0 );
}
}
/*
*
* manager对其分开管理
* manager管理start\stop等接口manager内部的inputManager和outputManager负责其他接口
* manager管理
*/
void MainWindow::on_pushButton_clicked()
{
/*这里是自定义输入、输出设备*/
QString name = QQtAudioManager::defaultInputDevice().deviceName();
if ( ui->inputListWidget->currentIndex().isValid() )
name = ui->inputListWidget->currentIndex().data().toString();
QAudioDeviceInfo dev = findInputAudioDeviceInfoByName ( name );
name = QQtAudioManager::defaultOutputDevice().deviceName();
if ( ui->outputListWidget->currentIndex().isValid() )
name = ui->outputListWidget->currentIndex().data().toString();
QAudioDeviceInfo devOut = findOutputAudioDeviceInfoByName ( name );
/*使用默认输入、输出设备*/
//如果开启这段代码,页面上的输入、输出设备选择,就仅仅是个显示了,不具备操作能力。
/*
dev = QQtAudioManager::defaultInputDevice();
devOut = QQtAudioManager::defaultOutputDevice();
*/
//把设备设进manager去
manager.inputDeviceInfo() = dev;
manager.outputDeviceInfo() = devOut;
//这里保证输入、输出使用格式相等 或者 不同
//如果格式不同在mac电脑上本地输入输出设备是可以使用的但是对于连接的语音蓝牙话筒却是不可以使用的原因未知。
//格式相同的时候,实在是太好用啦。
//这个建议默认就相同但是在QQtAudioManager当中并没有直接将其相等处理如果用户在readyRead槽函数里可以更改采样率进行某些特殊处理。一般不需要差异处理的相等就行了。
// int inBit = ui->inBit->currentIndex().data().toInt();
// int inChn = ui->inChn->currentIndex().data().toInt();
// int inRate = ui->intRate->currentIndex().data().toInt();
// QAudioFormat inFmt;
// inFmt.setChannelCount ( inChn );
// inFmt.setSampleSize ( inBit );
// inFmt.setSampleRate ( inRate );
// inFmt.setCodec ( "audio/pcm" );
// manager.inputAudioFormat() = inFmt;
QAudioFormat outFmt = manager.outputDeviceInfo().preferredFormat();
int outBit = outFmt.sampleSize(), outChn = outFmt.channelCount(), outRate = outFmt.sampleRate();
if ( ui->outBit->currentIndex().isValid() )
outBit = ui->outBit->currentIndex().data().toInt();
if ( ui->outChn->currentIndex().isValid() )
outChn = ui->outChn->currentIndex().data().toInt();
if ( ui->outRate->currentIndex().isValid() )
outRate = ui->outRate->currentIndex().data().toInt();
outFmt.setChannelCount ( outChn );
outFmt.setSampleSize ( outBit );
outFmt.setSampleRate ( outRate );
outFmt.setCodec ( "audio/pcm" );
manager.inputAudioFormat() = outFmt;
manager.outputAudioFormat() = outFmt;
pline() << "in prefer" << dev.preferredFormat().channelCount() << dev.preferredFormat().sampleRate() <<
dev.preferredFormat().sampleSize();
pline() << "out prefer" << devOut.preferredFormat().channelCount() << devOut.preferredFormat().sampleRate() <<
devOut.preferredFormat().sampleSize();
pline() << "in" << manager.inputAudioFormat().channelCount() << manager.inputAudioFormat().sampleRate() <<
manager.inputAudioFormat().sampleSize();
pline() << "out" << manager.outputAudioFormat().channelCount() << manager.outputAudioFormat().sampleRate() <<
manager.outputAudioFormat().sampleSize();
manager.startInput();
manager.startOutput();
}
void MainWindow::readyRead()
{
//这里是用户实现,任何用户希望做的事情,都在这里做完。
//可以 录音、保存文件
//可以 直接播放
//可以 混音 +保存 +播放...
//可以 消音
//可以 将pcm转换为其他格式音频
//可以 降噪
//可以 ...
//ptime();//11-12ms 是个10ms timer
QByteArray bytes = manager.readAll();
manager.write ( bytes );
}
void MainWindow::wavRecReadyRead()
{
QByteArray bytes = wavRecManager.readAll();
//pline() << "recording:" << bytes.size();
wavFileManager.write ( bytes );
}
void MainWindow::wavFileReadyRead()
{
QByteArray bytes = wavFileManager.readAll();
//pline() << "playing:" << bytes.size();
wavRecManager.write ( bytes );
}
void MainWindow::on_pushButton_3_clicked()
{
manager.stopInput();
manager.stopOutput();
}
/*bug:Qt 设置音量会报错退出*/
void MainWindow::on_inHS_valueChanged ( int value )
{
return;
if ( !manager.inputDevice() )
return;
qreal linearVolume;
// qreal linearVolume = QAudio::convertVolume ( value / qreal ( 100.0 ),
// QAudio::LogarithmicVolumeScale,
// QAudio::LinearVolumeScale );
// pline() << "输入音量" << value << linearVolume << qRound ( linearVolume * 100 ) ;
manager.inputManager()->setVolume ( qRound ( linearVolume * 100 ) );
}
/*bug:Qt 设置音量会报错退出*/
void MainWindow::on_outHS_valueChanged ( int value )
{
return;
if ( !manager.outputDevice() )
return;
qreal vol = qreal ( value ) / 100;
pline() << "输出音量" << vol ;
manager.outputManager()->setVolume ( vol );
}
void MainWindow::on_pushButton_4_clicked()
{
manager.inputAudioFormat() = QQtAudioManager::defaultOutputDevice().preferredFormat();
manager.outputAudioFormat() = QQtAudioManager::defaultOutputDevice().preferredFormat();
pline() << "in prefer" << QQtAudioManager::defaultInputDevice().preferredFormat().channelCount() <<
QQtAudioManager::defaultInputDevice().preferredFormat().sampleRate() <<
QQtAudioManager::defaultInputDevice().preferredFormat().sampleSize();
pline() << "out prefer" << QQtAudioManager::defaultOutputDevice().preferredFormat().channelCount() <<
QQtAudioManager::defaultOutputDevice().preferredFormat().sampleRate() <<
QQtAudioManager::defaultOutputDevice().preferredFormat().sampleSize();
pline() << "in" << manager.inputAudioFormat().channelCount() << manager.inputAudioFormat().sampleRate() <<
manager.inputAudioFormat().sampleSize();
pline() << "out" << manager.outputAudioFormat().channelCount() << manager.outputAudioFormat().sampleRate() <<
manager.outputAudioFormat().sampleSize();
manager.startDefaultInput();
manager.startDefaultOutput();
}
void MainWindow::on_pushButton_5_clicked()
{
QString name = QQtAudioManager::defaultOutputDevice().deviceName();
if ( ui->outputListWidget->currentIndex().isValid() )
name = ui->outputListWidget->currentIndex().data().toString();
QAudioDeviceInfo devOut = findOutputAudioDeviceInfoByName ( name );
QQtWavSoundEffect::Instance()->useCustomOutputDevice ( devOut );
QQtWavSound()->setLoops ( 3 );
QQtWavSound ( conf_res ( "9733.wav" ) );
}
void MainWindow::on_pushButton_6_clicked()
{
QString name = QQtAudioManager::defaultOutputDevice().deviceName();
if ( ui->outputListWidget->currentIndex().isValid() )
name = ui->outputListWidget->currentIndex().data().toString();
QAudioDeviceInfo devOut = findOutputAudioDeviceInfoByName ( name );
QQtWavSoundEffect::Instance()->useCustomOutputDevice ( devOut );
QQtWavSound ( conf_res ( "9763.wav" ) );
//QSound::play ( conf_res("9763.wav"));
}
void MainWindow::on_pushButton_7_clicked()
{
QString name = QQtAudioManager::defaultOutputDevice().deviceName();
if ( ui->outputListWidget->currentIndex().isValid() )
name = ui->outputListWidget->currentIndex().data().toString();
QAudioDeviceInfo devOut = findOutputAudioDeviceInfoByName ( name );
QQtWavSoundEffect::Instance()->useCustomOutputDevice ( devOut );
QQtWavSound()->setLoops ( 1 );
QQtWavSound ( conf_res ( "9612.wav" ) );
}
#ifdef __ANDROID__
#define TMPFILE "/sdcard/temp.wav"
#else
#define TMPFILE "./temp.wav"
#endif
void MainWindow::on_pushButton_8_clicked()
{
//不需要停止录音?需要
//android 不支持./temp.wav....
//record
QAudioDeviceInfo input = QQtAudioManager::defaultInputDevice();
wavRecManager.inputDeviceInfo() = input;
wavRecManager.inputAudioFormat() = input.preferredFormat();
//save wav file
wavFileManager.outputAudioFormat() = wavRecManager.inputAudioFormat();
wavFileManager.setOutputSourceFile ( TMPFILE );
pline() << "record:" << wavRecManager.inputDeviceInfo().deviceName() << wavRecManager.inputAudioFormat();
pline() << "save:" << wavFileManager.outputSourceFile() << wavFileManager.outputAudioFormat();
//内部存在自动关停。对wav写自动关停会导致文件被重写。这个函数是录音重新开始所以可以自动关停。
wavFileManager.startOutput();
wavRecManager.startInput();
}
void MainWindow::on_pushButton_9_clicked()
{
//不需要停止放音?需要
//read wav file
wavFileManager.setInputSourceFile ( TMPFILE );
//play record
wavRecManager.outputAudioFormat() = wavFileManager.inputAudioFormat();
wavRecManager.outputDeviceInfo() = QQtAudioManager::defaultOutputDevice();
pline() << "file:" << wavFileManager.inputSourceFile() << wavFileManager.inputAudioFormat();
pline() << "play:" << wavRecManager.outputDeviceInfo().deviceName() << wavRecManager.outputAudioFormat();
//内部存在自动关停。
wavRecManager.startOutput();
wavFileManager.startInput();
}
void MainWindow::on_pushButton_10_clicked()
{
wavRecManager.stopInput();
wavFileManager.stopOutput();
}
void MainWindow::on_pushButton_11_clicked()
{
wavFileManager.stopInput();
wavRecManager.stopOutput();
}
void MainWindow::on_pushButton_12_clicked()
{
//android support 16bit but 24bit
QQtWavSoundEffect* e0 = QQtWavSound();
e0->setTimerInterval ( 100 );
QQtWavSound ( conf_res ( "9767.wav" ) );
}

View File

@ -0,0 +1,67 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <qqtaudiomanager.h>
#include <qqtwavaudiomanager.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow ( QWidget* parent = 0 );
~MainWindow();
QAudioDeviceInfo findInputAudioDeviceInfoByName ( QString devName );
QAudioDeviceInfo findOutputAudioDeviceInfoByName ( QString devName );
private slots:
void on_pushButton_2_clicked();
void currentInputRowChanged ( QModelIndex, QModelIndex );
void currentOutputRowChanged ( QModelIndex, QModelIndex );
void on_pushButton_clicked();
void readyRead();
//录音到wav
void wavRecReadyRead();
//读取rec wav播放
void wavFileReadyRead();
void on_pushButton_3_clicked();
void on_inHS_valueChanged ( int value );
void on_outHS_valueChanged ( int value );
void on_pushButton_4_clicked();
void on_pushButton_5_clicked();
void on_pushButton_6_clicked();
void on_pushButton_7_clicked();
void on_pushButton_8_clicked();
void on_pushButton_9_clicked();
void on_pushButton_10_clicked();
void on_pushButton_11_clicked();
void on_pushButton_12_clicked();
private:
Ui::MainWindow* ui;
QQtAudioManager manager;
QQtAudioManager wavRecManager;
QQtWavAudioManager wavFileManager;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,273 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>751</width>
<height>367</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" rowspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QListWidget" name="inBit"/>
</item>
<item row="3" column="0">
<widget class="QListWidget" name="inChn"/>
</item>
<item row="3" column="2">
<widget class="QListWidget" name="intRate"/>
</item>
<item row="1" column="0" colspan="3">
<widget class="QListWidget" name="inputListWidget"/>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="label_2">
<property name="text">
<string>input format</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="text">
<string>input device</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0">
<widget class="QListWidget" name="outChn"/>
</item>
<item row="3" column="1">
<widget class="QListWidget" name="outBit"/>
</item>
<item row="3" column="2">
<widget class="QListWidget" name="outRate"/>
</item>
<item row="1" column="0" colspan="3">
<widget class="QListWidget" name="outputListWidget"/>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="label_4">
<property name="text">
<string>output format</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_3">
<property name="text">
<string>output device</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>start take voice and play</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_4">
<property name="text">
<string>start take voice
and play (default device)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>stop (optional)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>refresh device list</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Tab 2</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_5">
<property name="text">
<string>sound effect 1</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_6">
<property name="text">
<string>sound effect 2</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_7">
<property name="text">
<string>sound effect 3</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_12">
<property name="text">
<string>sound effect 4</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QWidget" name="widget_3" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QPushButton" name="pushButton_8">
<property name="text">
<string>record 0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_10">
<property name="text">
<string>stop record</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_9">
<property name="text">
<string>play record 0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_11">
<property name="text">
<string>stop playing</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="2">
<widget class="QWidget" name="widget_4" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>input volume</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="inHS">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>output volume</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="outHS">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>183</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>129</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Tab 3</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,8 @@
<RCC>
<qresource prefix="/">
<file alias="res/9733.wav">AppRoot/res/9733.wav</file>
<file alias="res/9612.wav">AppRoot/res/9612.wav</file>
<file alias="res/9763.wav">AppRoot/res/9763.wav</file>
<file alias="res/9767.wav">AppRoot/res/9767.wav</file>
</qresource>
</RCC>

View File

@ -0,0 +1,70 @@
#-------------------------------------------------
#
# Project created by QtCreator 2018-02-08T20:04:18
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = voicetest2
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
CONFIG += mobility
MOBILITY =
#这句话很重要 启动拷贝很多东西
system (touch main.cpp)
include (../../multi-link/multi-link/add_base_manager.pri)
contains(QSYS_PRIVATE, Android|AndroidX86) {
CONFIG += mobility
MOBILITY =
DISTFILES += \
$${PWD}/android/AndroidManifest.xml
ANDROID_PACKAGE_SOURCE_DIR = $${PWD}/android
}
RESOURCES += \
voicetest.qrc
#这个的设置有特点,要先设置
add_version (1,0,0,0)
#先发布App
#app从build到deploy
add_deploy()
#后发布依赖
#libQQt从sdk到build和deploy
add_dependent_manager(QQt)
#发布配置文件 把AppRoot里的配置项目拷贝到运行目录和发布目录
add_deploy_config($${PWD}/AppRoot)
message($$ANDROID_EXTRA_LIBS)