// 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 #include #include #include #include #include #include #ifdef PHONON_AUDIO #include #include #else #include #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(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(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 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(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.toStdString().c_str(), 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; }