From f2b8f6ddba1fac24644c0c2aa450e68a16c4c519 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Thu, 11 Mar 2021 14:44:40 -0500 Subject: [PATCH] 2021-03-11 Fred Gleason * Added a 'RDWaveFactory' class. Signed-off-by: Fred Gleason --- .gitignore | 1 + ChangeLog | 2 + lib/Makefile.am | 1 + lib/lib.pro | 2 + lib/librd_cs.ts | 8 ++ lib/librd_de.ts | 8 ++ lib/librd_es.ts | 8 ++ lib/librd_fr.ts | 8 ++ lib/librd_nb.ts | 8 ++ lib/librd_nn.ts | 8 ++ lib/librd_pt_BR.ts | 8 ++ lib/rdwavefactory.cpp | 158 ++++++++++++++++++++++++ lib/rdwavefactory.h | 48 ++++++++ tests/Makefile.am | 5 + tests/wavefactory_test.cpp | 242 +++++++++++++++++++++++++++++++++++++ tests/wavefactory_test.h | 72 +++++++++++ 16 files changed, 587 insertions(+) create mode 100644 lib/rdwavefactory.cpp create mode 100644 lib/rdwavefactory.h create mode 100644 tests/wavefactory_test.cpp create mode 100644 tests/wavefactory_test.h diff --git a/.gitignore b/.gitignore index d1b6b4c8..df65f1d2 100644 --- a/.gitignore +++ b/.gitignore @@ -138,6 +138,7 @@ tests/test_pam tests/timer_test tests/upload_test tests/wav_chunk_test +tests/wavefactory_test tests/wavescene_test tests/wavewidget_test utils/rdauth/rdauth diff --git a/ChangeLog b/ChangeLog index ea079e53..0313e16d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21262,3 +21262,5 @@ * Stubbed out a 'RDWaveScene' class. 2021-03-11 Fred Gleason * Added a 'RDWaveWidget' class. +2021-03-11 Fred Gleason + * Added a 'RDWaveFactory' class. diff --git a/lib/Makefile.am b/lib/Makefile.am index c22357db..6633749c 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -276,6 +276,7 @@ dist_librd_la_SOURCES = dbversion.h\ rdversion.cpp rdversion.h\ rdwavedata.cpp rdwavedata.h\ rdwavedata_dialog.cpp rdwavedata_dialog.h\ + rdwavefactory.cpp rdwavefactory.h\ rdwavefile.cpp rdwavefile.h\ rdwavescene.cpp rdwavescene.h\ rdwavepainter.cpp rdwavepainter.h\ diff --git a/lib/lib.pro b/lib/lib.pro index 40f3aa5d..fbde77bc 100644 --- a/lib/lib.pro +++ b/lib/lib.pro @@ -203,6 +203,7 @@ SOURCES += rduser.cpp SOURCES += rduserlistmodel.cpp SOURCES += rdversion.cpp SOURCES += rdwavedata.cpp +SOURCES += rdwavefactory.cpp SOURCES += rdwavefile.cpp SOURCES += rdwavescene.cpp SOURCES += rdwavewidget.cpp @@ -382,6 +383,7 @@ HEADERS += rduser.h HEADERS += rduserlistmodel.h HEADERS += rdversion.h HEADERS += rdwavedata.h +HEADERS += rdwavefactory.h HEADERS += rdwavescene.h HEADERS += rdwavewidget.h HEADERS += rdweb.h diff --git a/lib/librd_cs.ts b/lib/librd_cs.ts index ecd5dee1..c2bdddd4 100644 --- a/lib/librd_cs.ts +++ b/lib/librd_cs.ts @@ -921,6 +921,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_de.ts b/lib/librd_de.ts index cb96c0b2..47d58e64 100644 --- a/lib/librd_de.ts +++ b/lib/librd_de.ts @@ -917,6 +917,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_es.ts b/lib/librd_es.ts index 96d9ddbf..9023d9fa 100644 --- a/lib/librd_es.ts +++ b/lib/librd_es.ts @@ -917,6 +917,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_fr.ts b/lib/librd_fr.ts index ef93801f..90be116a 100644 --- a/lib/librd_fr.ts +++ b/lib/librd_fr.ts @@ -887,6 +887,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_nb.ts b/lib/librd_nb.ts index 0013b4d7..8da99af7 100644 --- a/lib/librd_nb.ts +++ b/lib/librd_nb.ts @@ -917,6 +917,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_nn.ts b/lib/librd_nn.ts index 0013b4d7..8da99af7 100644 --- a/lib/librd_nn.ts +++ b/lib/librd_nn.ts @@ -917,6 +917,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/librd_pt_BR.ts b/lib/librd_pt_BR.ts index bd4d8378..6b39e43d 100644 --- a/lib/librd_pt_BR.ts +++ b/lib/librd_pt_BR.ts @@ -917,6 +917,14 @@ Output + + No such cart/cut! + + + + Energy export failed + + RDAddCart diff --git a/lib/rdwavefactory.cpp b/lib/rdwavefactory.cpp new file mode 100644 index 00000000..4c5c52f6 --- /dev/null +++ b/lib/rdwavefactory.cpp @@ -0,0 +1,158 @@ +// rdwavefactory.cpp +// +// Factory for generating audio waveform pixmaps +// +// (C) Copyright 2021 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include + +#include "rdapplication.h" +#include "rdcut.h" +#include "rdpeaksexport.h" +#include "rdwavefactory.h" + +RDWaveFactory::RDWaveFactory(RDWaveFactory::TrackMode mode) +{ + d_track_mode=mode; + d_cart_number=0; + d_cut_number=-1; +} + + +RDWaveFactory::TrackMode RDWaveFactory::trackMode() const +{ + return d_track_mode; +} + + +unsigned RDWaveFactory::cartNumber() const +{ + return d_cart_number; +} + + +int RDWaveFactory::cutNumber() const +{ + return d_cut_number; +} + + +QPixmap RDWaveFactory::generate(int height,int x_shrink,int gain) +{ + QPixmap pix(d_energy.size()/(x_shrink*d_energy_channels),height); + pix.fill(Qt::white); // FIXME: make the background transparent + QPainter *p=new QPainter(&pix); + p->setPen(Qt::black); + + // + // Gain Ratio + // + double ratio=exp10((double)gain/2000.0); + + // + // Waveform + // + for(unsigned i=0;idrawLine(0,zero_line,d_energy.size()/x_shrink,zero_line); + for(int j=i;jlvl) { + lvl=d_energy.at(j+k); + } + } + int rlvl=(int)(ratio*(double)lvl*(double)height/ + (65534.0*(double)d_energy_channels)); + // Bottom half + p->fillRect(j/(x_shrink*d_energy_channels),zero_line,1,rlvl,Qt::black); + + // Top half + p->fillRect(j/(x_shrink*d_energy_channels),zero_line,1,-rlvl,Qt::black); + } + } + + // + // Dividing Line + // + p->setPen(Qt::gray); + for(unsigned i=1;idrawLine(0,i*height/d_energy_channels, + d_energy.size()/x_shrink,i*height/d_energy_channels); + } + + p->end(); + delete p; + + return pix; +} + + +bool RDWaveFactory::setCut(QString *err_msg,unsigned cartnum,int cutnum) +{ + d_energy.clear(); + d_cart_number=cartnum; + d_cut_number=cutnum; + + // + // Get Cut Info + // + RDCut *cut=new RDCut(cartnum,cutnum); + if(!cut->exists()) { + *err_msg=QObject::tr("No such cart/cut!"); + delete cut; + return false; + } + d_channels=cut->channels(); + delete cut; + d_energy_channels=d_channels; + if(d_track_mode==RDWaveFactory::SingleTrack) { + d_energy_channels=1; + } + + // + // Get Cut Energy Data + // + RDPeaksExport::ErrorCode err_code; + RDPeaksExport *conv=new RDPeaksExport(); + + conv->setCartNumber(cartnum); + conv->setCutNumber(cutnum); + if((err_code=conv->runExport(rda->user()->name(),rda->user()->password()))!= + RDPeaksExport::ErrorOk) { + *err_msg=QObject::tr("Energy export failed")+": "+ + RDPeaksExport::errorText(err_code); + delete conv; + return false; + } + if((d_track_mode==RDWaveFactory::SingleTrack)&&(d_channels==2)) { // Mix-down + for(unsigned i=0;ienergySize();i+=2) { + uint32_t frame= + ((uint32_t)conv->energy(i)+(uint32_t)conv->energy(i+1))/2; + d_energy.push_back(frame); + } + } + else { // Pass-through + for(unsigned i=0;ienergySize();i++) { + d_energy.push_back(conv->energy(i)); + } + } + delete conv; + + return true; +} diff --git a/lib/rdwavefactory.h b/lib/rdwavefactory.h new file mode 100644 index 00000000..a89d2fd8 --- /dev/null +++ b/lib/rdwavefactory.h @@ -0,0 +1,48 @@ +// rdwavefactory.h +// +// Factory for generating audio waveform pixmaps +// +// (C) Copyright 2021 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef RDWAVEFACTORY_H +#define RDWAVEFACTORY_H + +#include +#include + +class RDWaveFactory +{ + public: + enum TrackMode {SingleTrack=0,MultiTrack=1}; + RDWaveFactory(TrackMode mode); + TrackMode trackMode() const; + unsigned cartNumber() const; + int cutNumber() const; + QPixmap generate(int height,int x_shrink,int gain); + bool setCut(QString *err_msg,unsigned cartnum,int cutnum); + + private: + TrackMode d_track_mode; + unsigned d_cart_number; + int d_cut_number; + QList d_energy; + unsigned d_channels; + unsigned d_energy_channels; +}; + + +#endif // RDWAVEFACTORY_H diff --git a/tests/Makefile.am b/tests/Makefile.am index 2f8b4abc..8d9715af 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -53,6 +53,7 @@ noinst_PROGRAMS = audio_convert_test\ timer_test\ upload_test\ wav_chunk_test\ + wavefactory_test\ wavescene_test\ wavewidget_test @@ -142,6 +143,10 @@ upload_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ dist_wav_chunk_test_SOURCES = wav_chunk_test.cpp wav_chunk_test.h wav_chunk_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ +dist_wavefactory_test_SOURCES = wavefactory_test.cpp wavefactory_test.h +nodist_wavefactory_test_SOURCES = moc_wavefactory_test.cpp +wavefactory_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ + dist_wavescene_test_SOURCES = wavescene_test.cpp wavescene_test.h nodist_wavescene_test_SOURCES = moc_wavescene_test.cpp wavescene_test_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ diff --git a/tests/wavefactory_test.cpp b/tests/wavefactory_test.cpp new file mode 100644 index 00000000..eb4d786c --- /dev/null +++ b/tests/wavefactory_test.cpp @@ -0,0 +1,242 @@ +// wavefactory_test.cpp +// +// Test harness for RDWavefactory +// +// (C) Copyright 2021 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include +#include + +#include +#include + +#include "wavefactory_test.h" + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent) +{ + QString err_msg; + bool ok=false; + + d_cart_number=0; + d_cut_number=-1; + d_scene=NULL; + bool track_mode_set=false; + + // + // Open the database + // + rda=new RDApplication("wavefactory_test","wavefactory_test",WAVEFACTORY_TEST_USAGE, + this); + if(!rda->open(&err_msg)) { + QMessageBox::critical(this,"wavefactory_test - "+tr("Error"),err_msg); + exit(RDApplication::ExitNoDb); + } + d_font_engine=new RDFontEngine(font(),rda->config()); + + // + // Get Command Switches + // + for(unsigned i=0;icmdSwitch()->keys();i++) { + if(rda->cmdSwitch()->key(i)=="--cart-number") { + d_cart_number=rda->cmdSwitch()->value(i).toUInt(&ok); + if((!ok)||(d_cart_number>RD_MAX_CART_NUMBER)) { + fprintf(stderr,"wavefactory_test: invalid cart number\n"); + exit(RDApplication::ExitInvalidOption); + } + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--cut-number") { + d_cut_number=rda->cmdSwitch()->value(i).toInt(&ok); + if((!ok)||(d_cut_number>RD_MAX_CUT_NUMBER)||(d_cut_number<1)) { + fprintf(stderr,"wavefactory_test: invalid cut number\n"); + exit(RDApplication::ExitInvalidOption); + } + rda->cmdSwitch()->setProcessed(i,true); + } + if(rda->cmdSwitch()->key(i)=="--track-mode") { + if(rda->cmdSwitch()->value(i)=="single") { + d_track_mode=RDWaveFactory::SingleTrack; + track_mode_set=true; + } + if(rda->cmdSwitch()->value(i)=="multi") { + d_track_mode=RDWaveFactory::MultiTrack; + track_mode_set=true; + } + if(!track_mode_set) { + fprintf(stderr,"wavefactory_test: invalid --track-mode argument\n"); + exit(RDApplication::ExitInvalidOption); + } + rda->cmdSwitch()->setProcessed(i,true); + } + + if(!rda->cmdSwitch()->processed(i)) { + fprintf(stderr,"wavefactory_test: unknown option \"%s\"\"\n", + rda->cmdSwitch()->key(i).toUtf8().constData()); + exit(RDApplication::ExitInvalidOption); + } + } + if(d_cart_number==0) { + fprintf(stderr,"wavefactory_test: you must specify a cart number\n"); + exit(RDApplication::ExitInvalidOption); + } + if(d_cut_number==-1) { + fprintf(stderr,"wavefactory_test: you must specify a cut number\n"); + exit(RDApplication::ExitInvalidOption); + } + if(!track_mode_set) { + fprintf(stderr,"wavefactory_test: you must specify a track mode\n"); + exit(RDApplication::ExitInvalidOption); + } + + // + // Wave Display + // + d_factory=new RDWaveFactory(d_track_mode); + d_view=new QGraphicsView(this); + + // + // X Shrink Factor + // + d_shrink_factor_group=new QGroupBox(tr("X Shrink Factor"),this); + d_shrink_factor_group->setFont(d_font_engine->labelFont()); + d_up_button= + new RDTransportButton(RDTransportButton::Up,d_shrink_factor_group); + connect(d_up_button,SIGNAL(clicked()),this,SLOT(upShrinkData())); + d_down_button= + new RDTransportButton(RDTransportButton::Down,d_shrink_factor_group); + connect(d_down_button,SIGNAL(clicked()),this,SLOT(downShrinkData())); + d_shrink_factor_edit=new QLineEdit(d_shrink_factor_group); + d_shrink_factor_edit->setReadOnly(true); + d_shrink_factor_edit->setText("1"); + + // + // Audio Gain + // + d_audio_gain_label=new QLabel(tr("Audio Gain")+":",this); + d_audio_gain_label->setFont(d_font_engine->labelFont()); + d_audio_gain_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + d_audio_gain_spin=new QSpinBox(this); + d_audio_gain_spin->setRange(0,100); + d_audio_gain_unit=new QLabel(tr("dB")+":",this); + d_audio_gain_unit->setFont(d_font_engine->labelFont()); + d_audio_gain_unit->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + connect(d_audio_gain_spin,SIGNAL(valueChanged(int)), + this,SLOT(gainChangedData(int))); + + // + // Connect to ripcd(8) + // + connect(rda,SIGNAL(userChanged()),this,SLOT(userData())); + rda->ripc()-> + connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password()); + + setMinimumSize(sizeHint()); + setMaximumHeight(sizeHint().height()); +} + + +QSize MainWidget::sizeHint() const +{ + return QSize(1000,375); +} + + +void MainWidget::userData() +{ + QString err_msg; + + setWindowTitle("wavefactory_test User: "+rda->user()->name()); + if(!d_factory->setCut(&err_msg,d_cart_number,d_cut_number)) { + QMessageBox::critical(this,"wavefactory_test - "+tr("Error"),err_msg); + exit(RDApplication::ExitInvalidOption); + } + UpdateWave(); +} + + +void MainWidget::upShrinkData() +{ + int x_shrink=d_shrink_factor_edit->text().toInt(); + + x_shrink=x_shrink*2; + d_shrink_factor_edit->setText(QString().sprintf("%d",x_shrink)); + UpdateWave(); +} + + +void MainWidget::downShrinkData() +{ + int x_shrink=d_shrink_factor_edit->text().toInt(); + + if(x_shrink>1) { + x_shrink=x_shrink/2; + d_shrink_factor_edit->setText(QString().sprintf("%d",x_shrink)); + UpdateWave(); + } +} + + +void MainWidget::gainChangedData(int db) +{ + UpdateWave(); +} + + +void MainWidget::resizeEvent(QResizeEvent *e) +{ + int w=size().width(); + int h=size().height(); + + d_view->setGeometry(0,0,w,20+d_view->sizeHint().height()); + + d_shrink_factor_group->setGeometry(10,h-150,155,145); + d_up_button->setGeometry(10,25,80,50); + d_down_button->setGeometry(10,80,80,50); + d_shrink_factor_edit->setGeometry(100,25,50,20); + + d_audio_gain_label->setGeometry(200,h-120,100,20); + d_audio_gain_spin->setGeometry(305,h-120,50,20); + d_audio_gain_unit->setGeometry(360,h-120,50,20); +} + + +void MainWidget::UpdateWave() +{ + QPixmap pix=d_factory->generate(200,d_shrink_factor_edit->text().toInt(), + 100*d_audio_gain_spin->value()); + + if(d_scene!=NULL) { + d_scene->deleteLater(); + } + d_scene=new QGraphicsScene(0,0,pix.width(),pix.height()); + d_scene->addPixmap(pix); + d_view->setScene(d_scene); + d_view->setGeometry(0,0,size().width(),20+d_view->sizeHint().height()); +} + + +int main(int argc,char *argv[]) +{ + QApplication a(argc,argv); + + MainWidget *w=new MainWidget(); + w->show(); + + return a.exec(); +} diff --git a/tests/wavefactory_test.h b/tests/wavefactory_test.h new file mode 100644 index 00000000..53853807 --- /dev/null +++ b/tests/wavefactory_test.h @@ -0,0 +1,72 @@ +// wavefactory_test.h +// +// Test harness for RDWaveFactory +// +// (C) Copyright 2021 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef WAVEFACTORY_TEST_H +#define WAVEFACTORY_TEST_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define WAVEFACTORY_TEST_USAGE "--cart-number= --cut-number= --track-mode=single|multi\n" + +class MainWidget : public QWidget +{ + Q_OBJECT + public: + MainWidget(QWidget *parent=0); + QSize sizeHint() const; + + private slots: + void userData(); + void upShrinkData(); + void downShrinkData(); + void gainChangedData(int db); + + protected: + void resizeEvent(QResizeEvent *e); + + private: + void UpdateWave(); + QGraphicsScene *d_scene; + QGraphicsView *d_view; + RDWaveFactory *d_factory; + unsigned d_cart_number; + int d_cut_number; + RDWaveFactory::TrackMode d_track_mode; + RDFontEngine *d_font_engine; + RDTransportButton *d_up_button; + RDTransportButton *d_down_button; + QLineEdit *d_shrink_factor_edit; + QGroupBox *d_shrink_factor_group; + QLabel *d_audio_gain_label; + QSpinBox *d_audio_gain_spin; + QLabel *d_audio_gain_unit; +}; + + +#endif // WAVEFACTORY_TEST_H