467 lines
14 KiB
C++
467 lines
14 KiB
C++
// DocumentWindow class definition
|
|
//
|
|
// The widget that aggregate all the views of an ATS document :
|
|
// - the partials list view
|
|
// - the partials spectrogram view
|
|
// - the residual list view
|
|
// - the residual spectrogram view
|
|
// and the associated controls (sliders, splitters, buttons, rulers, ...)
|
|
//
|
|
// QATSH Copyright 2009 Jean-Philippe MEURET <jpmeuret@free.fr>
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
|
|
#include <QtCore/QSettings>
|
|
|
|
#include <QtGui/QItemSelection>
|
|
#include <QtGui/QItemSelectionModel>
|
|
#include <QtGui/QMessageBox>
|
|
|
|
#ifdef PHONON_AUDIO
|
|
#include <Phonon/MediaObject>
|
|
#include <Phonon/MediaSource>
|
|
#else
|
|
#include <QtCore/QProcess>
|
|
#endif
|
|
|
|
#include "TypesAndConstants.h"
|
|
|
|
#include "ATSMath.h"
|
|
|
|
#include "ATSModel.h"
|
|
#include "ATSModelItems.h"
|
|
#include "ATSModelManager.h"
|
|
|
|
#include "IATSAnalysis.h"
|
|
#include "StandardAnalysis.h"
|
|
#include "ATSSynthesis.h"
|
|
#include "SampledSound.h"
|
|
#include "ATSSound.h"
|
|
|
|
#include "MainWindow.h"
|
|
#include "FilePropertiesDialog.h"
|
|
#include "TableAndToolsWidget.h"
|
|
|
|
#include "DocumentWindow.h"
|
|
#include "ui_DocumentWindow.h"
|
|
|
|
|
|
DocumentWindow::DocumentWindow(QWidget *parent)
|
|
: QWidget(parent), _pMainWin(static_cast<MainWindow*>(parent)),
|
|
_pModelMgr(0), _eCurrGraphView(ePartialsSpectrogram),
|
|
_eLastSyntFormat((SampledSound::ESampleFormat)-1), _pLastSyntParams(0)
|
|
{
|
|
_pui = new Ui::DocumentWindow;
|
|
_pui->setupUi(this);
|
|
}
|
|
|
|
DocumentWindow::~DocumentWindow()
|
|
{
|
|
delete _pui;
|
|
if (_pModelMgr)
|
|
delete _pModelMgr;
|
|
if (_pLastSyntParams)
|
|
delete _pLastSyntParams;
|
|
}
|
|
|
|
void DocumentWindow::connectModelAndViews()
|
|
{
|
|
// Give the model to the views (TableAndTools first, for correct selection model exchange).
|
|
_pui->qwTableAndTools->setModelManager(_pModelMgr);
|
|
_pui->qwPartialsSpecgram->setModelManager(_pModelMgr);
|
|
_pui->qwResidualsSpecgram->setModelManager(_pModelMgr);
|
|
|
|
// Connect user interactions to local slots.
|
|
connect(_pui->qwTableAndTools, SIGNAL(reAnalyse(const QString&, IATSAnalysis*)),
|
|
this, SLOT(onReAnalyse(const QString&, IATSAnalysis*)));
|
|
|
|
connect(_pui->qwTableAndTools, SIGNAL(reSynthesise()), this, SLOT(onReSynthesise()));
|
|
connect(_pui->qwTableAndTools, SIGNAL(startPlayingSynthesisResult()),
|
|
this, SLOT(onStartPlayingSynthesisResult()));
|
|
connect(_pui->qwTableAndTools, SIGNAL(stopPlayingSynthesisResult()),
|
|
this, SLOT(onStopPlayingSynthesisResult()));
|
|
|
|
// Put select/edit tool front.
|
|
showSelectionEditionTool();
|
|
|
|
// Set main window size from settings, now we have something inside.
|
|
QSettings qSettings;
|
|
const QSize qSize = qSettings.value("Window/Size", QSize(800, 600)).toSize();
|
|
_pMainWin->resize(qSize);
|
|
}
|
|
|
|
void DocumentWindow::onModelReset()
|
|
{
|
|
//std::cout << "DocumentWindow::onModelReset()" << std::endl;
|
|
_pMainWin->setWindowModified(true);
|
|
_pMainWin->enableAction(MainWindow::eSaveFile, true);
|
|
}
|
|
|
|
void DocumentWindow::onModelChanged(const QModelIndex&, const QModelIndex&)
|
|
{
|
|
//std::cout << "DocumentWindow::onModelChanged()" << std::endl;
|
|
_pMainWin->setWindowModified(true);
|
|
_pMainWin->enableAction(MainWindow::eSaveFile, true);
|
|
}
|
|
|
|
void DocumentWindow::changeEvent(QEvent *e)
|
|
{
|
|
switch (e->type()) {
|
|
case QEvent::LanguageChange:
|
|
_pui->retranslateUi(this);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool DocumentWindow::loadFile(const QString &qsFileName)
|
|
{
|
|
// Can not be called twice.
|
|
_pModelMgr = new ATSModelManager();
|
|
|
|
// Load model from file.
|
|
const bool bLoaded = _pModelMgr->mainModel()->load(qsFileName);
|
|
|
|
// Be ready for model changes / reset (but ignore this first one ;-).
|
|
connect(_pModelMgr->mainModel(),
|
|
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
|
this, SLOT(onModelChanged(const QModelIndex&, const QModelIndex&)));
|
|
connect(_pModelMgr->mainModel(), SIGNAL(modelReset()), this, SLOT(onModelReset()));
|
|
|
|
// Connect models and views.
|
|
if (bLoaded)
|
|
{
|
|
connectModelAndViews();
|
|
_pMainWin->enableAction(MainWindow::eSaveFileAs, true);
|
|
_pMainWin->enableAction(MainWindow::eFileProperties, true);
|
|
_pMainWin->enableAction(MainWindow::eReSynthesize, true);
|
|
_pMainWin->enableAction(MainWindow::eWinPartials, true);
|
|
_pMainWin->enableAction(MainWindow::eWinResidual, true);
|
|
}
|
|
|
|
return bLoaded;
|
|
}
|
|
|
|
bool DocumentWindow::storeFile(const QString &qsFileName)
|
|
{
|
|
// Try and store data to the target file.
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
const bool bStored = _pModelMgr->mainModel()->store(qsFileName);
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
if (bStored)
|
|
_pMainWin->enableAction(MainWindow::eSaveFile, false);
|
|
|
|
return bStored;
|
|
}
|
|
|
|
bool DocumentWindow::analyseFile(const QString &qsFileName, IATSAnalysis* pAnalysis)
|
|
{
|
|
// Note: May be called more than once (re-analysis).
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
// Load sampled sound from file.
|
|
ATSSound* pATSSound = 0;
|
|
SampledSound smpSound;
|
|
if (smpSound.load(qsFileName.toStdString().c_str()))
|
|
{
|
|
if (pAnalysis->isOfType(StandardAnalysis::typeName()))
|
|
{
|
|
// Run analysis
|
|
StandardAnalysis* pStdAnalysis = static_cast<StandardAnalysis*>(pAnalysis);
|
|
pATSSound = (*pStdAnalysis)(&smpSound);
|
|
}
|
|
else
|
|
{
|
|
std::cout << "DocumentWindow::analyseFile : Unsupported analysis type" << std::endl;
|
|
}
|
|
|
|
// Setup model if analysis was successfull.
|
|
if (pATSSound)
|
|
{
|
|
const bool bFirstTime = (_pModelMgr == 0);
|
|
|
|
if (bFirstTime)
|
|
{
|
|
// Create model manager (and managed models)
|
|
_pModelMgr = new ATSModelManager();
|
|
|
|
// Be ready for model changes / reset.
|
|
connect(_pModelMgr->mainModel(),
|
|
SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
|
this, SLOT(onModelChanged(const QModelIndex&, const QModelIndex&)));
|
|
connect(_pModelMgr->mainModel(), SIGNAL(modelReset()),
|
|
this, SLOT(onModelReset()));
|
|
}
|
|
|
|
// Set model data.
|
|
_pModelMgr->mainModel()->setSound(pATSSound);
|
|
|
|
// On first time analysis :
|
|
if (bFirstTime)
|
|
{
|
|
// Connect models and views
|
|
connectModelAndViews();
|
|
|
|
// As we have got here through the analysis dialog/wizard,
|
|
// copy associated analysis settings into the analysis tool (same widget inside).
|
|
_pui->qwTableAndTools->setAnalysisSampleFile(qsFileName);
|
|
_pui->qwTableAndTools->setAnalysisParams(pAnalysis);
|
|
}
|
|
|
|
// Enable relevant actions.
|
|
_pMainWin->enableAction(MainWindow::eSaveFileAs, true);
|
|
_pMainWin->enableAction(MainWindow::eFileProperties, true);
|
|
_pMainWin->enableAction(MainWindow::eReAnalyse, true);
|
|
_pMainWin->enableAction(MainWindow::eReSynthesize, true);
|
|
_pMainWin->enableAction(MainWindow::eWinPartials, true);
|
|
_pMainWin->enableAction(MainWindow::eWinResidual, true);
|
|
}
|
|
}
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
return pATSSound != 0;
|
|
}
|
|
|
|
bool DocumentWindow::synthesiseFile(const QString &qsFileName,
|
|
SampledSound::ESampleFormat eFormat,
|
|
ATSSynthesis* pSynthesis)
|
|
{
|
|
// Note: May be called more than once (re-synthesis).
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
// Get selected partials if necessary.
|
|
std::vector<int> vecSelPartIndexes;
|
|
if (pSynthesis->useOnlySelectedPartials())
|
|
{
|
|
// Retrieve the selected partial indexes if any.
|
|
const QModelIndexList qlmiSelParts =
|
|
_pModelMgr->partialsSelectionModel()->selection().indexes();
|
|
|
|
// Convert this model indexes to ATSSynthesis indexes (ignore non-ePartial type indexes).
|
|
for (int nSelPartInd = 0; nSelPartInd < qlmiSelParts.size(); nSelPartInd++)
|
|
{
|
|
const QModelIndex qmiSelPart = qlmiSelParts.at(nSelPartInd);
|
|
if (qmiSelPart.isValid() && qmiSelPart.internalPointer()
|
|
&& static_cast<const ATSModelItem*>(qmiSelPart.internalPointer())->type()
|
|
== ATSModelItem::ePartial)
|
|
vecSelPartIndexes.push_back(qmiSelPart.column());
|
|
}
|
|
}
|
|
|
|
// Check synthesis parameters : if none changed and if synthesis must use all partials,
|
|
// nothing to do again. Same if none changed and if synthesis must use only selected partials,
|
|
// and partial selection did change.
|
|
bool bDone = false;
|
|
if (qsFileName == _qsLastSyntResFileName
|
|
&& *pSynthesis == *_pLastSyntParams && eFormat == _eLastSyntFormat
|
|
&& (!pSynthesis->useOnlySelectedPartials()
|
|
|| vecSelPartIndexes == _vecLastSelParts))
|
|
bDone = true;
|
|
|
|
// Finally, if really a need for re-synthesis, do it.
|
|
ATSSound* pATSSound = _pModelMgr->mainModel()->sound();
|
|
if (!bDone && pATSSound)
|
|
{
|
|
// Do the synthesis.
|
|
SampledSound* pSampSound = (*pSynthesis)(pATSSound, vecSelPartIndexes);
|
|
if (pSampSound)
|
|
{
|
|
// Save the sampled sound to the given file.
|
|
// TODO: Customizable sample format.
|
|
bDone = pSampSound->store(qsFileName.toAscii(), eFormat);
|
|
|
|
// Free now useless sampled sound.
|
|
delete pSampSound;
|
|
|
|
// Save a copy of the synthesis inputs for later.
|
|
_qsLastSyntResFileName = qsFileName;
|
|
if (!_pLastSyntParams)
|
|
_pLastSyntParams = new ATSSynthesis();
|
|
*_pLastSyntParams = *pSynthesis;
|
|
_eLastSyntFormat = eFormat;
|
|
_vecLastSelParts = vecSelPartIndexes;
|
|
}
|
|
}
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
return bDone;
|
|
}
|
|
|
|
void DocumentWindow::showFileProperties()
|
|
{
|
|
if (_pModelMgr)
|
|
{
|
|
FilePropertiesDialog filePropsDlg(this);
|
|
filePropsDlg.setModelManager(_pModelMgr);
|
|
filePropsDlg.exec();
|
|
}
|
|
}
|
|
|
|
void DocumentWindow::showSelectionEditionTool()
|
|
{
|
|
_pui->qwTableAndTools->switchTool(TableAndToolsWidget::eSelectionEdition);
|
|
}
|
|
|
|
void DocumentWindow::showAnalysisTool()
|
|
{
|
|
_pui->qwTableAndTools->switchTool(TableAndToolsWidget::eAnalysis);
|
|
}
|
|
|
|
void DocumentWindow::showSynthesisTool()
|
|
{
|
|
_pui->qwTableAndTools->switchTool(TableAndToolsWidget::eSynthesis);
|
|
}
|
|
|
|
void DocumentWindow::showGraphicalView(DocumentWindow::EGraphicalView eView)
|
|
{
|
|
if (_pModelMgr)
|
|
{
|
|
// Toggle data table (on the left)
|
|
const TypesAndConstants::EDataType eDataType =
|
|
(eView == eResidualsSpectrogram)
|
|
? TypesAndConstants::eResidualBand : TypesAndConstants::ePartial;
|
|
_pui->qwTableAndTools->switchSelectEditDataList(eDataType);
|
|
|
|
// Toggle graphical view (on the right)
|
|
_pui->qswGraphicalViews->setCurrentIndex(eView);
|
|
}
|
|
}
|
|
|
|
void DocumentWindow::onReAnalyse(const QString& qsFileName, IATSAnalysis* pAnaParams)
|
|
{
|
|
if (!analyseFile(qsFileName, pAnaParams))
|
|
QMessageBox::warning(this, qApp->applicationName(),
|
|
tr("Re-analysis failed for file %1.").arg(qsFileName));
|
|
else
|
|
_pMainWin->setWindowModified(true);
|
|
}
|
|
|
|
void DocumentWindow::onReSynthesise()
|
|
{
|
|
// Get synthesis parameters.
|
|
QString qsFileName;
|
|
SampledSound::ESampleFormat eFormat;
|
|
ATSSynthesis* pSyntParams;
|
|
|
|
_pui->qwTableAndTools->getSynthesisParams(qsFileName, eFormat, pSyntParams);
|
|
|
|
if (!synthesiseFile(qsFileName, eFormat, pSyntParams))
|
|
QMessageBox::warning(this, qApp->applicationName(),
|
|
tr("Re-synthesis failed for file %1.").arg(qsFileName));
|
|
}
|
|
|
|
void DocumentWindow::onStartPlayingSynthesisResult()
|
|
{
|
|
// Get synthesis parameters.
|
|
QString qsFileName;
|
|
SampledSound::ESampleFormat eFormat;
|
|
ATSSynthesis* pSyntParams;
|
|
|
|
_pui->qwTableAndTools->getSynthesisParams(qsFileName, eFormat, pSyntParams);
|
|
|
|
// Re-synthesise (if necessary) and play the synthesized sound.
|
|
if (synthesiseFile(qsFileName, eFormat, pSyntParams))
|
|
{
|
|
#ifdef PHONON_AUDIO
|
|
// Through a Phonon media object (never tested).
|
|
Phonon::MediaObject *pmoSound =
|
|
Phonon::createPlayer(Phonon::MusicCategory, Phonon::MediaSource(qsFileName));
|
|
pmoSound->play();
|
|
delete pmoSound;
|
|
#else
|
|
// Through an external audio player :
|
|
// 1) Get the player command from settings
|
|
QSettings qSettings;
|
|
const QString qsAudioPlayerCmd =
|
|
qSettings.value("AudioPlayer/Command").toString().arg(qsFileName);
|
|
std::cout << "DocumentWindow::onStartPlayingSynthesisResult : qsAudioPlayerCmd='" << qsAudioPlayerCmd.toStdString() << "'" << std::endl;
|
|
|
|
// 2) Start playing.
|
|
QProcess qpPlayer(this);
|
|
//qpPlayer.setStandardOutputFile("audio-out.log");
|
|
if (!qpPlayer.startDetached(qsAudioPlayerCmd))
|
|
QMessageBox::warning(this, qApp->applicationName(),
|
|
tr("Failed to play synthesized sound\n"
|
|
"through command '%1'\nCheck player command preference.")
|
|
.arg(qsAudioPlayerCmd));
|
|
#endif
|
|
}
|
|
else
|
|
QMessageBox::warning(this, qApp->applicationName(),
|
|
tr("Re-synthesis failed for file %1.").arg(qsFileName));
|
|
}
|
|
|
|
void DocumentWindow::onStopPlayingSynthesisResult()
|
|
{
|
|
std::cout << "DocumentWindow::onStopPlayingSynthesisResult : Not implemented" << std::endl;
|
|
}
|
|
|
|
void DocumentWindow::zoomIn()
|
|
{
|
|
// Determine currently shown view.
|
|
const EGraphicalView eView =
|
|
(EGraphicalView)_pui->qswGraphicalViews->currentIndex();
|
|
|
|
// Zoom it as requested (only frequency here).
|
|
switch (eView)
|
|
{
|
|
case ePartialsSpectrogram:
|
|
_pui->qwPartialsSpecgram->zoom(0.5, 1.0, 0.5, sqrt(2.0));
|
|
break;
|
|
case eResidualsSpectrogram:
|
|
_pui->qwResidualsSpecgram->zoom(0.5, 1.0, 0.5, sqrt(2.0));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DocumentWindow::zoomOut()
|
|
{
|
|
// Determine currently shown view.
|
|
const EGraphicalView eView =
|
|
(EGraphicalView)_pui->qswGraphicalViews->currentIndex();
|
|
|
|
// Zoom it as requested (only frequency here).
|
|
switch (eView)
|
|
{
|
|
case ePartialsSpectrogram:
|
|
_pui->qwPartialsSpecgram->zoom(0.5, 1.0, 0.5, 1.0 / sqrt(2.0));
|
|
break;
|
|
case eResidualsSpectrogram:
|
|
_pui->qwResidualsSpecgram->zoom(0.5, 1.0, 0.5, 1.0 / sqrt(2.0));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DocumentWindow::zoomAll()
|
|
{
|
|
// Determine currently shown view.
|
|
const EGraphicalView eView =
|
|
(EGraphicalView)_pui->qswGraphicalViews->currentIndex();
|
|
|
|
// Zoom it as requested (only frequency here).
|
|
switch (eView)
|
|
{
|
|
case ePartialsSpectrogram:
|
|
_pui->qwPartialsSpecgram->zoomAll();
|
|
break;
|
|
case eResidualsSpectrogram:
|
|
_pui->qwResidualsSpecgram->zoomAll();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DocumentWindow::zoomSelection()
|
|
{
|
|
std::cout << "DocumentWindow::zoomSelection : Not implemented" << std::endl;
|
|
}
|
|
|