首先,源代码在这里下载
utau-qt重写,gitee
是个写着玩的项目。
实现这个项目,我首先得知道以下几个东西,wav,提取wav音调的算法等等。
wav的解释已经很多了,懂得都懂,网上信息很多,只想说,懂得都懂。
struct wav_struct
{
unsigned long file_size; //文件大小
unsigned short channel; //通道数
unsigned long frequency; //采样频率
unsigned long Bps; //Byte率
unsigned short sample_num_bit; //一个样本的位数
unsigned int data_size; //数据大小
unsigned char* data; //音频数据 ,这里要定义什么就看样本位数了,我这里只是单纯的复制数据
};
这是读取代码的结构体,看一下就知道怎么回事了
很多人查找wav的变调算法,最后找到这个
https://www.cnblogs.com/cpuimage/p/8321878.html
wavBuffer = wavRead_float(in_file, &sampleRate, &totalSampleCount);
这一段这个函数我也没有找到。。。233333wav.h的历史版本也没有。不过这个代码很简单,其实就是把波形文件转换成float数组而已2333,所以有没有这个
另外
#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
DR_WAVE_FORMAT_IEEE_FLOAT这玩意在dr_wav.h里面就是一个define的玩意,所以这个头文件我们是可以摆脱滴
for (unsigned long k =0; k<(int)WAV2.data_size*sounds[i].split; k = k + 2)
{
//右边为大端
unsigned long data_low = WAV2.data[k];
unsigned long data_high = WAV2.data[k + 1];
double data_true = data_high * 256 + data_low;
long data_complement = 0;
//取大端的最高位(符号位)
int my_sign = (int)(data_high / 128);
//printf("%d ", my_sign);
if (my_sign == 1)
{
data_complement = data_true - 65536;
}
else
{
data_complement = data_true;
}
//printf("%d ", data_complement);
setprecision(4);
double float_data = (double)(data_complement/(double)32768);
//cout<<k/2<<endl;
hex2[k/2]=float_data*sounds[i].sound;//响度设置,如你所见
//cout<<hex2[k/4]<<endl;
这个代码是另一个项目。。。2333,开源中国找到的,但是忘记是谁了。由于double是八个字节,所以里所应当数组是data的一半23333
然后得到的数组就是buffer了!
smbPitchShift(pitchShift, (WAV2.data_size/2)*sounds[i].split, 2048, 4, WAV2.frequency, hex2, hex2);//我这个split是切片操作,把信息切片,utau嘛。如果你需要用可以删掉
又是一个开源项目,smbPitchShift,有年头了。虽然算法看不懂(建议先学习fft),但是这个函数会用就行了。pitchShift这个是升降调的元素。
现在进入gui的地方。gui选择了我喜欢(会用的)qt。
设计也蛮简单的,就是套了两层qwiget,里面不绑定,有其他用处。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->resize(200,200);//重置主窗口
ui->widget -> installEventFilter(this);//设置发信装置(应该是这个叫法?)
edit = new QLineEdit;//这个得new,所以我头文件定义了一改QLineEdit
hasPress = false;//这个是控制不随意生成新控件的函数
lastPnt = QPoint(0, 0);
ui->tab->setMinimumSize(200,200);//设置tab最小界面
QPalette palette = ui->widget->palette();//这玩意设置背景
//palette.setBrush(QPalette::Window,QBrush(QColor(61,61,61)));
palette.setBrush(QPalette::Background,QBrush(QPixmap(":/new/prefix1/text.png")));//2333这个图片是一个xlsx里面截取出来的,利用qt背景图片平铺的鬼畜特性
ui->widget->setAutoFillBackground(true);
ui->widget->setPalette(palette);
QScrollArea *m_pScroll = new QScrollArea(ui->tab);
m_pScroll->setWidget(ui->widget);//给widget_2设置滚动条
ui->widget->setMinimumSize(4400,4700);//这里注意,要比主窗体的尺寸要大,不然太小的话会留下一片空白
QHBoxLayout *pLayout = new QHBoxLayout;//这个很重要
pLayout->addWidget(m_pScroll);
pLayout->setMargin(0);
pLayout->setSpacing(0);
ui->tab->setLayout(pLayout);
// 初始化button并设置button的大小和位置。
}
bool MainWindow::eventFilter(QObject * b, QEvent *evt)
{
QMouseEvent* e = static_cast<QMouseEvent*>(evt);
if(b==edit)
{
//qDebug()<<"yes we did";
if (evt->type()==QEvent::FocusOut) // 这里指 lineEdit1 控件的失去焦点事件
{
clearFocus();
edit->close();
}
}else if(b==ui->widget)
{
if(evt->type()==QEvent::MouseButtonPress && e->button()==Qt::LeftButton)
{
static int i=0;
//emit clicked();
qDebug() << i;
if (hasPress )
{
exit;
}
if (i>0 && e->x()-10<button3[i-1]->x()+button3[i-1]->width())
{
exit;
}
if(i==0)
{
button3.push_back(new QPushButton("sss",ui->widget));
change.push_back(new QPushButton("ch",ui->widget));
//button2=new QPushButton("sss",ui->widget);
button3[i] -> resize(200, 50);
change[i]->resize(20,50);
button3[i] -> setGeometry(e->x(), (e->y()/50)*50,button3[i]->width(), button3[i]->height());
change[i] -> setGeometry(e->x()+button3[i]->width()-20, (e->y()/50)*50,change[i]->width(), change[i]->height());
button3[i] -> installEventFilter(this);
button3[i]->show();
change[i]->installEventFilter(this);
change[i]->show();
i++;
}
else {
if(e->x()>button3[i-1]->x()+button3[i-1]->width())
{
button3.push_back(new QPushButton("sss",ui->widget));
change.push_back(new QPushButton("ch",ui->widget));
//button2=new QPushButton("sss",ui->widget);
button3[i] -> resize(200, 50);
change[i]->resize(20,50);
button3[i] -> setGeometry(e->x(), (e->y()/50)*50,button3[i]->width(), button3[i]->height());
change[i] -> setGeometry(e->x()+button3[i]->width()-20, (e->y()/50)*50,change[i]->width(), change[i]->height());
button3[i] -> installEventFilter(this);
button3[i]->show();
change[i]->installEventFilter(this);
change[i]->show();
i++;
}
else {
exit;
}
}
}
}
// else if(b==)
else
{
//if (watched ==edit)
int a=0,c=-1,h=0,n=-1;
for(int i=0;i<button3.size();i++)
{
if(b==button3[i])
{
a=1;
c=i;
}
if(b==change[i])
{
h=1;
n=i;
}
}
if(a==1 && evt->type() == QEvent::MouseButtonPress) // 按下鼠标左键
{
if(e->button() == Qt::LeftButton){ //鼠标左键点击
lastPnt = e -> pos();
hasPress = true;
}
if(e->button()==Qt::RightButton)
{
// qDebug()<<"ss";
//q=1;
//signalMapper = new QSignalMapper(this);
q=c;
hasPress=true;
edit->setParent(button3[c]);
// qDebug()<<"ss";
QString ButtonText = button3[c]->text();
edit->setText(ButtonText);
edit->resize(button3[c]->size());
edit->show();
edit->setFocus();
edit->selectAll();
edit->installEventFilter(this);
// QPushButton::mouseDoubleClickEvent(event);
}
}else if(a==1 && evt->type() == QEvent::MouseMove && hasPress)
{
// QMouseEvent* e = static_cast<QMouseEvent*>(evt);
// 计算新的移动后的位置
QPoint movePoint = e->pos()-lastPnt + QPoint(button3[c]->geometry().x(), button3[c]->geometry().y());
// 设置可移动的X和Y的范围
//bool moveX = movePoint.x() > 0 && movePoint.x() < width() - button -> width();
//bool moveY = movePoint.y() > 0 && movePoint.y() < height() - button -> height();
bool moveX;
if(c==0)
{
moveX = movePoint.x() > 0 && movePoint.x()<4400;
if (button3.size()!=c+1)
{
moveX = movePoint.x() > 0 && movePoint.x()<4400 && movePoint.x()<button3[1]->x()-button3[1]->width();
}
}
else {
moveX = movePoint.x() > button3[c-1]->x()+button3[c-1]->width() && movePoint.x()<4400;
if (button3.size()!=c+1)
{
moveX = movePoint.x() > button3[c-1]->x()+button3[c-1]->width() && movePoint.x()<4400 && movePoint.x()<button3[c+1]->x()-button3[c+1]->width();
}
}
bool moveY = movePoint.y() > 0 && movePoint.y()<4650;
if(moveX && moveY){ // 在X和Y的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
button3[c] -> move(movePoint);
change[c] -> setGeometry(button3[c]->x()+button3[c]->width()-20, button3[c]->y(),change[c]->width(), change[c]->height());
}else if(moveX){ // 在X的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
button3[c] -> move(movePoint.x(), button3[c] -> pos().y());
change[c] -> setGeometry(button3[c]->x()+button3[c]->width()-20, button3[c]->y(),change[c]->width(), change[c]->height());
}else if(moveY){ // 在Y的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
button3[c] -> move(button3[c]->pos().x(), movePoint.y());
change[c] -> setGeometry(button3[c]->x()+button3[c]->width()-20, button3[c]->y(),change[c]->width(), change[c]->height());
}else{
// do nothing
}
}
//萌萌答的分割线
if(h==1 && evt->type() == QEvent::MouseButtonPress) // 按下鼠标左键
{
if(e->button() == Qt::LeftButton){ //鼠标左键点击
lastPnt = e -> pos();
hasPress = true;
}
}else if(h==1 && evt->type() == QEvent::MouseMove && hasPress)
{
qDebug()<<h<<a;
// QMouseEvent* e = static_cast<QMouseEvent*>(evt);
// 计算新的移动后的位置
QPoint movePoint = e->pos()-lastPnt + QPoint(change[n]->geometry().x(), change[n]->geometry().y());
qDebug()<<h<<a;
// 设置可移动的X和Y的范围
//bool moveX = movePoint.x() > 0 && movePoint.x() < width() - button -> width();
//bool moveY = movePoint.y() > 0 && movePoint.y() < height() - button -> height();
bool moveX;
if(n==0)
{
moveX = movePoint.x() > button3[n]->x()+10 && movePoint.x()<4400;
if (change.size()!=n+1)
{
moveX = movePoint.x() > button3[n]->x()+10 && movePoint.x()<4400 && movePoint.x()<change[1]->x()-button3[1]->width();
}
}
else {
moveX = movePoint.x() > button3[n]->x()+10 && movePoint.x()<4400;
if (change.size()!=n+1)
{
moveX = movePoint.x() > button3[n]->x()+10 && movePoint.x()<4400 && movePoint.x()<change[n+1]->x()-button3[n+1]->width();
}
}
bool moveY = movePoint.y() ==change[n]->y();
if(moveX && moveY){ // 在X和Y的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
change[n] -> move(movePoint);
int l=change[n]->x()-button3[n]->x();
button3[n]->resize(l+10,50);
}else if(moveX){ // 在X的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
change[n] -> move(movePoint.x(), change[n] -> pos().y());
int l=change[n]->x()-button3[n]->x();
button3[n]->resize(l+10,50);
}else if(moveY){ // 在Y的允许范围内
//int x=(movePoint.rx()/121)*121;
int y=(movePoint.ry()/50)*50;
//movePoint.setX(x);
movePoint.setY(y);
change[n] -> move(change[n]->pos().x(), movePoint.y());
int l=change[n]->x()-button3[n]->x();
button3[n]->resize(l+10,50);
}else{
// do nothing
}
}
return false;
}
}
事件过滤器全部,有些长。。。我也不太想解释。这个事件过滤器里面有点击widget生成button的事件,我控制了button出现和移动的位置。关于像utau一样调节边缘调整长度的我使用了另一个button数组,在button3每一个产生的地方的末尾生成button,然后根据两个属于不同button数组的button resize button3控件
void MainWindow::on_pushButton_clicked()
{
qDebug()<<"start"<<endl;
int o=0;
int x = button3.size();
qDebug()<<"x="<<x<<endl;
infomation test[100];
for (int i=0;i<x;i++)
{
if(i==0 && button3[0]->x()>20)
{
int j = button3[0]->x();
int e = j/200;
float r = (float)(j-e*200)/200;
cout<<r<<endl;
cout<<"ss"<<endl;
for(int h=1;h<=e;h++)
{
test[o].name="empty.wav";test[o].sound=2;test[o].tone=0;test[o].split=1;
o++;
}
test[o].name="empty.wav";test[o].sound=2;test[o].tone=0;test[o].split=r;
o++;
}
else if(i!=0 && button3[i]->x()-button3[i-1]->x()-button3[i-1]->width()>20)
{
int j = button3[i]->x()-button3[i-1]->x()-button3[i-1]->width();
int e = j/200;
float r = (float)(j-e*200)/200;
cout<<r<<endl;
cout<<"ss"<<endl;
for(int h=1;h<=e;h++)
{
test[o].name="empty.wav";test[o].sound=2;test[o].tone=0;test[o].split=1;
o++;
}
test[o].name="empty.wav";test[o].sound=2;test[o].tone=0;test[o].split=r;
o++;
}
string shift = MainWindow::turnstring(button3[i]->text());
string u=shift+".wav";
char p[10];
strcpy(p,u.c_str());
qDebug()<<p<<endl;
qDebug()<<"here: "<<button3[i]->text()<<endl;
wav_struct text =get_wav(p);
float f = ((float)button3[i]->width())/((((float)text.data_size)/449104)*200);
qDebug()<<p;
int t=button3[i]->width();
if(f>1)
{
f=1;
t=((((float)text.data_size)/449104)*200);
qDebug()<<"ssss"<<t;
}
qDebug()<<"test"<<p<<endl;
cout<<"but"<<endl;
test[o].name=p;test[o].sound=2;test[o].tone=button3[i]->y()/50-10;test[o].split=f;
//qDebug()<<"finally"<<i<<test[i].name;
o++;
cout<<"o:"<<o<<endl<<endl;
}
cout<<1<<endl;
//cout<<test[0].name<<test[1].name<<test[2].name<<"yes";
make(test,o);
}
这一段是合成的部分。由于手动生成不知道出了啥毛病。。。。我选择用一个空音频文件合成。。。就这样了